├── .gitignore
├── .idea
├── .name
├── .rakeTasks
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── encodings.xml
├── misc.xml
├── modules.xml
└── vcs.xml
├── .rspec
├── .ruby-gemset
├── .ruby-version
├── Gemfile
├── Gemfile.lock
├── Guardfile
├── LICENSE
├── README.md
├── Rakefile
├── cucumber.yml
├── dummy_box
├── Cucumber_Vagrantfile
├── Vagrantfile
├── Vagrantfile_qemu
├── dummy.box
├── metadata.json
└── provision.sh
├── features
├── destroy_command.feature
├── halt_command.feature
├── provision_command.feature
├── replace_iso_file.feature
├── replace_template_file.feature
├── ssh_command.feature
├── ssh_run_command.feature
├── status_command.feature
├── step_definitions
│ └── general_steps.rb
├── support
│ ├── env.rb
│ ├── file_helper.rb
│ ├── machine_helper.rb
│ ├── template_helper.rb
│ ├── time_helper.rb
│ ├── vagrant_process_mock.rb
│ ├── vagrant_ui_mock.rb
│ └── vagrant_util_ssh_mock.rb
├── template_file.feature
├── up_command.feature
├── upload_iso_file.feature
└── upload_template_file.feature
├── lib
├── required_parameters.rb
├── sanity_checks.rb
├── vagrant-proxmox.rb
└── vagrant-proxmox
│ ├── action.rb
│ ├── action
│ ├── adjust_forwarded_port_params.rb
│ ├── cleanup_after_destroy.rb
│ ├── clone_vm.rb
│ ├── config_clone.rb
│ ├── connect_proxmox.rb
│ ├── create_vm.rb
│ ├── destroy_vm.rb
│ ├── get_node_list.rb
│ ├── is_created.rb
│ ├── is_stopped.rb
│ ├── message_already_running.rb
│ ├── message_already_stopped.rb
│ ├── message_file_not_found.rb
│ ├── message_not_created.rb
│ ├── message_not_running.rb
│ ├── message_upload_server_error.rb
│ ├── proxmox_action.rb
│ ├── read_ssh_info.rb
│ ├── read_state.rb
│ ├── select_node.rb
│ ├── shutdown_vm.rb
│ ├── start_vm.rb
│ ├── stop_vm.rb
│ ├── sync_folders.rb
│ ├── upload_iso_file.rb
│ └── upload_template_file.rb
│ ├── config.rb
│ ├── errors.rb
│ ├── plugin.rb
│ ├── provider.rb
│ ├── proxmox
│ ├── connection.rb
│ └── errors.rb
│ └── version.rb
├── local_test.rb
├── locales
└── en.yml
├── spec
├── actions
│ ├── cleanup_after_destroy_action_spec.rb
│ ├── connect_proxmox_action_spec.rb
│ ├── create_vm_action_spec.rb
│ ├── destroy_vm_action_spec.rb
│ ├── get_node_list_action_spec.rb
│ ├── is_created_action_spec.rb
│ ├── is_stopped_action_spec.rb
│ ├── message_already_running_action_spec.rb
│ ├── message_already_stopped_action_spec.rb
│ ├── message_file_not_found_spec.rb
│ ├── message_not_created_action_spec.rb
│ ├── message_not_running_action_spec.rb
│ ├── message_upload_server_error_spec.rb
│ ├── proxmox_action_shared.rb
│ ├── proxmox_action_spec.rb
│ ├── read_ssh_info_action_spec.rb
│ ├── read_state_action_spec.rb
│ ├── select_node_spec.rb
│ ├── shutdown_vm_action_spec.rb
│ ├── start_vm_action_spec.rb
│ ├── stop_vm_action_spec.rb
│ ├── sync_folders_action_spec.rb
│ ├── upload_iso_file_action_spec.rb
│ └── upload_template_file_action_spec.rb
├── commands
│ ├── destroy_command_spec.rb
│ ├── halt_command_spec.rb
│ ├── provision_command_spec.rb
│ ├── ssh_command_spec.rb
│ ├── ssh_run_command_spec.rb
│ ├── status_command_spec.rb
│ └── up_command_spec.rb
├── config_spec.rb
├── plugin_spec.rb
├── provider_spec.rb
├── proxmox
│ ├── connection_spec.rb
│ └── rest_call_shared.rb
├── sanity_checks_spec.rb
├── spec_helper.rb
└── spec_helpers
│ ├── common_helpers.rb
│ └── time_helpers.rb
├── vagrant-proxmox.gemspec
└── vagrant-proxmox.iml
/.gitignore:
--------------------------------------------------------------------------------
1 | # If you find yourself ignoring temporary files generated by your text editor
2 | # or operating system, you probably want to add a global ignore instead:
3 | # git config --global core.excludesfile ~/.gitignore_global
4 |
5 | # Ignore bundler config and gem cache
6 | /.bundle
7 | /bin
8 |
9 | /Vagrantfile
10 | /Vagrantfile_qemu
11 |
12 | # Ignore all logfiles and tempfiles.
13 | /.vagrant
14 | /tmp
15 | /.tmp
16 | /.build
17 | /pkg
18 | *.gem
19 | *~
20 |
21 | # Ignore generated coverage reports
22 | /coverage
23 |
24 | # IntelliJ
25 | .iws
26 | .idea/workspace.xml
27 | .idea/tasks.xml
28 | .idea/gradle.xml
29 | .idea/dataSources.*
30 | atlassian-ide-plugin.xml
31 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | vagrant-proxmox
--------------------------------------------------------------------------------
/.idea/.rakeTasks:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | 1.8
21 |
22 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --color
2 | --backtrace
3 |
--------------------------------------------------------------------------------
/.ruby-gemset:
--------------------------------------------------------------------------------
1 | vagrant-proxmox
2 |
--------------------------------------------------------------------------------
/.ruby-version:
--------------------------------------------------------------------------------
1 | ruby-2.2.1
2 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | gemspec
4 |
5 | group :development do
6 | #We depend on Vagrant for development, but we don't add it as a
7 | #gem dependency because we expect to be installed within the
8 | #Vagrant environment itself using `vagrant plugin`.
9 | gem "vagrant", '1.4.3',
10 | :git => 'https://github.com/mitchellh/vagrant.git',
11 | :ref => 'v1.4.3'
12 | end
13 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GIT
2 | remote: https://github.com/mitchellh/vagrant.git
3 | revision: 4f0eb9504cc786d5a57a43814427e8eb35407a4c
4 | ref: v1.4.3
5 | specs:
6 | vagrant (1.4.3)
7 | childprocess (~> 0.3.7)
8 | erubis (~> 2.7.0)
9 | i18n (~> 0.6.0)
10 | log4r (~> 1.1.9)
11 | net-scp (~> 1.1.0)
12 | net-ssh (>= 2.6.6, < 2.8.0)
13 |
14 | PATH
15 | remote: .
16 | specs:
17 | vagrant-proxmox (0.0.9)
18 | activesupport (~> 4.0.0)
19 | rest-client (~> 1.6.7)
20 | retryable (~> 1.3.3)
21 |
22 | GEM
23 | remote: https://rubygems.org/
24 | specs:
25 | activesupport (4.0.13)
26 | i18n (~> 0.6, >= 0.6.9)
27 | minitest (~> 4.2)
28 | multi_json (~> 1.3)
29 | thread_safe (~> 0.1)
30 | tzinfo (~> 0.3.37)
31 | addressable (2.4.0)
32 | awesome_print (1.2.0)
33 | builder (3.2.2)
34 | childprocess (0.3.9)
35 | ffi (~> 1.0, >= 1.0.11)
36 | coderay (1.1.1)
37 | crack (0.4.3)
38 | safe_yaml (~> 1.0.0)
39 | cucumber (1.3.20)
40 | builder (>= 2.1.2)
41 | diff-lcs (>= 1.1.3)
42 | gherkin (~> 2.12)
43 | multi_json (>= 1.7.5, < 2.0)
44 | multi_test (>= 0.1.2)
45 | diff-lcs (1.2.5)
46 | docile (1.1.5)
47 | erubis (2.7.0)
48 | ffi (1.9.10)
49 | formatador (0.2.5)
50 | geminabox (0.11.1)
51 | builder
52 | httpclient (>= 2.2.7)
53 | nesty
54 | sinatra (>= 1.2.7)
55 | gherkin (2.12.2)
56 | multi_json (~> 1.3)
57 | guard (2.13.0)
58 | formatador (>= 0.2.4)
59 | listen (>= 2.7, <= 4.0)
60 | lumberjack (~> 1.0)
61 | nenv (~> 0.1)
62 | notiffany (~> 0.0)
63 | pry (>= 0.9.12)
64 | shellany (~> 0.0)
65 | thor (>= 0.18.1)
66 | guard-rspec (4.2.10)
67 | guard (~> 2.1)
68 | rspec (>= 2.14, < 4.0)
69 | httpclient (2.7.1)
70 | i18n (0.6.11)
71 | libnotify (0.8.4)
72 | ffi (>= 1.0.11)
73 | listen (3.0.6)
74 | rb-fsevent (>= 0.9.3)
75 | rb-inotify (>= 0.9.7)
76 | log4r (1.1.10)
77 | lumberjack (1.0.10)
78 | method_source (0.8.2)
79 | mime-types (1.25.1)
80 | minitest (4.7.5)
81 | multi_json (1.11.2)
82 | multi_test (0.1.2)
83 | nenv (0.3.0)
84 | nesty (1.0.2)
85 | net-scp (1.1.2)
86 | net-ssh (>= 2.6.5)
87 | net-ssh (2.7.0)
88 | notiffany (0.0.8)
89 | nenv (~> 0.1)
90 | shellany (~> 0.0)
91 | pry (0.10.3)
92 | coderay (~> 1.1.0)
93 | method_source (~> 0.8.1)
94 | slop (~> 3.4)
95 | rack (1.6.4)
96 | rack-protection (1.5.3)
97 | rack
98 | rake (10.5.0)
99 | rb-fsevent (0.9.7)
100 | rb-inotify (0.9.7)
101 | ffi (>= 0.5.0)
102 | rest-client (1.6.9)
103 | mime-types (~> 1.16)
104 | retryable (1.3.6)
105 | rspec (3.0.0)
106 | rspec-core (~> 3.0.0)
107 | rspec-expectations (~> 3.0.0)
108 | rspec-mocks (~> 3.0.0)
109 | rspec-core (3.0.4)
110 | rspec-support (~> 3.0.0)
111 | rspec-expectations (3.0.4)
112 | diff-lcs (>= 1.2.0, < 2.0)
113 | rspec-support (~> 3.0.0)
114 | rspec-mocks (3.0.4)
115 | rspec-support (~> 3.0.0)
116 | rspec-support (3.0.4)
117 | safe_yaml (1.0.4)
118 | shellany (0.0.1)
119 | simplecov (0.9.2)
120 | docile (~> 1.1.0)
121 | multi_json (~> 1.0)
122 | simplecov-html (~> 0.9.0)
123 | simplecov-html (0.9.0)
124 | simplecov-rcov (0.2.3)
125 | simplecov (>= 0.4.1)
126 | sinatra (1.4.7)
127 | rack (~> 1.5)
128 | rack-protection (~> 1.4)
129 | tilt (>= 1.3, < 3)
130 | slop (3.6.0)
131 | thor (0.19.1)
132 | thread_safe (0.3.5)
133 | tilt (2.0.2)
134 | timecop (0.7.4)
135 | tzinfo (0.3.47)
136 | webmock (1.18.0)
137 | addressable (>= 2.3.6)
138 | crack (>= 0.3.2)
139 |
140 | PLATFORMS
141 | ruby
142 |
143 | DEPENDENCIES
144 | awesome_print (~> 1.2.0)
145 | cucumber (~> 1.3.15)
146 | geminabox (~> 0.11.1)
147 | guard-rspec (= 4.2.10)
148 | libnotify (~> 0.8.3)
149 | rake (= 10.5.0)
150 | rspec (~> 3.0.0)
151 | simplecov (~> 0.9.0)
152 | simplecov-rcov (~> 0.2.3)
153 | timecop (~> 0.7.1)
154 | vagrant (= 1.4.3)!
155 | vagrant-proxmox!
156 | webmock (~> 1.18.0)
157 |
158 | BUNDLED WITH
159 | 1.11.2
160 |
--------------------------------------------------------------------------------
/Guardfile:
--------------------------------------------------------------------------------
1 | # A sample Guardfile
2 | # More info at https://github.com/guard/guard#readme
3 |
4 | guard :rspec do
5 | watch(%r{^spec/.+_spec\.rb$})
6 | watch('spec/spec_helper.rb') { "spec" }
7 | watch(%r{^lib/vagrant-proxmox/action/(.+)\.rb$}) { |m| "spec/actions/#{m[1]}_action_spec.rb" }
8 | watch(%r{^lib/vagrant-proxmox/proxmox/(.+)\.rb$}) { |m| "spec/proxmox/#{m[1]}_spec.rb" }
9 | end
10 |
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all 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,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Vagrant Proxmox Provider
2 |
3 | This is a [Vagrant](http://www.vagrantup.com) plugin that adds a
4 | [Proxmox](http://proxmox.com/) provider to Vagrant, allowing Vagrant to manage
5 | and provision Proxmox virtual machines.
6 |
7 | ## Features
8 |
9 | * Create/Destroy OpenVZ containers from specified templates
10 | * Start/Shutdown OpenVZ containers
11 | * Create/Destroy Qemu containers from specified templates or iso file
12 | * Start/Shutdown Qemu containers
13 | * SSH into virtual machine
14 | * Provision the virtual machine
15 | * Synced folder support via rsync
16 |
17 | ## Limitations
18 |
19 | * For OpenVZ containers you need a Vagrant compatible OpenVZ template
20 | * For OpenVZ containers only routed network mode is currently supported
21 | * For KVM machines the ISO file needs to be a Vagrant compatible live system or automatic installation
22 | * For KVM machines the Qemu template has to be on the selected_node
23 |
24 | ## Requirements
25 |
26 | * Vagrant 1.5+
27 | * Ruby 2+
28 |
29 | ## Installation
30 |
31 | Install using standard Vagrant plugin method:
32 |
33 | ```
34 | $ vagrant plugin install vagrant-proxmox
35 | ```
36 |
37 | This will install the plugin from [RubGems.org](http://rubygems.org/).
38 |
39 | ## Usage
40 |
41 | First install the provided dummy vagrant box:
42 |
43 | ```
44 | $ vagrant box add dummy dummy_box/dummy.box
45 | ```
46 |
47 | Then for an openvz container create a Vagrantfile that looks like the following (note that you might have to add "@pam" to your username if you're getting a "401 Unauthorized" error):
48 |
49 | ```
50 | Vagrant.configure('2') do |config|
51 |
52 | config.vm.provider :proxmox do |proxmox|
53 | proxmox.endpoint = 'https://your.proxmox.server:8006/api2/json'
54 | proxmox.user_name = 'proxmox_username@pam'
55 | proxmox.password = 'proxmox_password'
56 | proxmox.vm_id_range = 900..910
57 | proxmox.vm_name_prefix = 'vagrant_'
58 | proxmox.openvz_os_template = 'local:vztmpl/vagrant-proxmox-ubuntu-12.tar.gz'
59 | proxmox.vm_type = :openvz
60 | proxmox.vm_memory = 256
61 | end
62 |
63 | config.vm.define :box, primary: true do |box|
64 | box.vm.box = 'dummy'
65 | box.vm.network :public_network, ip: '192.168.0.1'
66 | end
67 |
68 | end
69 | ```
70 |
71 | If you want KVM the Vagrantfile could look as follows:
72 |
73 | ```
74 | Vagrant.configure('2') do |config|
75 |
76 | config.vm.provider :proxmox do |proxmox|
77 | proxmox.endpoint = 'https://proxmox.example.com/api2/json'
78 | proxmox.user_name = 'vagrant'
79 | proxmox.password = 'password'
80 | proxmox.vm_id_range = 900..910
81 | proxmox.vm_type = :qemu
82 | proxmox.vm_name_prefix = 'vagrant_'
83 | proxmox.qemu_os = :l26
84 | proxmox.qemu_disk_size = '30G'
85 | proxmox.qemu_storage = 'local'
86 | proxmox.qemu_iso_file = '/home/user/system.iso'
87 | proxmox.vm_name_prefix = 'vagrant_test_'
88 | proxmox.qemu_cores = 1
89 | proxmox.qemu_sockets = 1
90 | proxmox.qemu_nic_model = 'virtio'
91 | proxmox.qemu_bridge = 'vmbr0'
92 | proxmox.vm_memory = 512
93 | end
94 |
95 | config.vm.define :box, primary: true do |box|
96 | box.vm.box = 'dummy'
97 | box.vm.network :public_network, ip: '192.168.0.1', macaddress: 'ff:aa:cc:dd:bb:ee'
98 | end
99 |
100 | end
101 | ```
102 |
103 | For the meaning of the various options, refer to the `Options` section below.
104 |
105 | You need an OpenVZ template or KVM ISO that contains a vagrant user supplied with the default Vagrant SSH keys.
106 | You can download an example Ubuntu based template [here](https://www.dropbox.com/s/vuzywdosxhjjsag/vagrant-proxmox-ubuntu-12.tar.gz).
107 |
108 | Finally run `vagrant up --provider=proxmox` to create and start the new OpenVZ container.
109 |
110 | ## Options
111 |
112 | * `endpoint` URL of the JSON API endpoint of your Proxmox installation
113 | * `user_name` The name of the Proxmox user that Vagrant should use
114 | * `password` The password of the above user
115 | * `vm_id_range` The possible range of machine ids. The smallest free one is chosen for a new machine
116 | * `vm_name_prefix` An optional string that is prepended before the vm name
117 | * `vm_type` The virtual machine type, e.g. :openvz or :qemu
118 | * `openvz_os_template` The name of the template from which the OpenVZ container should be created
119 | * `openvz_template_file` The openvz os template file to upload and use for the virtual machine (can be specified instead of `openvz_os_template`)
120 | * `replace_openvz_template_file` Set to true if the openvz os template file should be replaced on the server (default: false)
121 | * `vm_memory` The container's main memory size
122 | * `task_timeout` How long to wait for completion of a Proxmox API command (in seconds)
123 | * `task_status_check_interval` Interval in seconds between checking for completion of a Proxmox API command
124 | * `ssh_timeout` The maximum timeout for a ssh connection to a virtual machine (in seconds)
125 | * `ssh_status_check_interval` The interval between two ssh reachability status retrievals (in seconds)
126 | * `imgcopy_timeout` The maximum timeout for a proxmox server task in case it's an upload (in seconds)
127 | * `qemu_os` The qemu virtual machine operating system, e.g. :l26
128 | * `qemu_iso` The qemu iso file to use for the virtual machine
129 | * `qemu_iso_file` The qemu iso file to upload and use for the virtual machine (can be specified instead of `qemu_iso`)
130 | * `replace_qemu_iso_file` Set to true if the iso file should be replaced on the server (default: false)
131 | * `replace_template` Set to true if the iso file should be replaced on the server (default: false)
132 | * `qemu_template` The name of a qemu template which is used to create a clone (can be specified instead of `qemu_iso[_file]`)
133 | * `qemu_disk_size` The qemu disk size to use for the virtual machine, e.g. '30G'
134 | * `qemu_storage` The storage pool to use, i.e. the value of the `storage` key of the hash returned by `pvesh get /nodes/{node}/storage`, e.g. 'raid', 'local', 'cephstore'
135 | * `qemu_cores` The number of cores per socket available to the VM
136 | * `qemu_sockets` The number of CPU sockets available to the VM
137 | * `qemu_nic_model` which model of network interface card to use, default 'e1000'
138 | * `qemu_bridge` connect automatically to this bridge, default 'vmbr0'
139 | * `selected_node` If specified, only this specific node is used to create machines
140 |
141 | ## Build the plugin
142 |
143 | Build the plugin gem with
144 |
145 | ```
146 | $ rake build
147 | ```
148 |
149 | Optionally run the rspec tests with
150 |
151 |
152 | ```
153 | $ rake spec
154 | ```
155 |
156 | ## About us
157 |
158 | [TELCAT MULTICOM GmbH](http://www.telcat.com) is a Germany-wide system house for innovative solutions and
159 | services in the areas of information, communication and security technology.
160 |
161 | We develop IP-based telecommunication systems ([TELCAT-UC](http://www.telcat.de/TELCAT-R-UC.304.0.html)) and
162 | use Vagrant and Proxmox to automatically deploy and test the builds in our Jenkins jobs.
163 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require 'rake'
2 | require 'rake/clean'
3 | require 'rubygems'
4 | require 'bundler/setup'
5 | require 'rubygems/gem_runner'
6 | require 'rspec/core/rake_task'
7 | require 'geminabox_client'
8 | require 'yaml'
9 |
10 | gemspec = eval(File.read 'vagrant-proxmox.gemspec')
11 |
12 | desc 'Build the project'
13 | task :build do
14 | Gem::GemRunner.new.run ['build', "#{gemspec.name}.gemspec"]
15 | end
16 |
17 | RSpec::Core::RakeTask.new
18 |
19 | desc 'Run RSpec code examples with coverage'
20 | RSpec::Core::RakeTask.new('spec_coverage') do |_|
21 | ENV['RUN_WITH_COVERAGE'] = 'true'
22 | end
23 |
24 | task :release_local do
25 | rake_config = YAML::load(File.read("#{ENV['HOME']}/.rake/rake.yml")) rescue {}
26 | GeminaboxClient.new(rake_config['geminabox']['url']).push "#{gemspec.name}-#{gemspec.version}.gem", overwrite: true
27 | puts "Gem #{gemspec.name} pushed to #{rake_config['geminabox']['url']}"
28 | end
29 |
30 | task :release_rubygems do
31 | `gem push "#{gemspec.name}-#{gemspec.version}.gem"`
32 | end
33 |
34 | task release_all: [:release_local, :release_rubygems]
35 |
36 | namespace :jenkins do
37 | task job: [:build, :spec_coverage, :release_all]
38 | end
39 |
40 | namespace :test do
41 |
42 | desc 'Run all tests (enable coverage with COVERAGE=y)'
43 | task :all do
44 | Rake::Task['test:rspec'].invoke
45 | Rake::Task['test:cucumber'].invoke
46 | end
47 |
48 | desc 'Run all rspec tests (enable coverage with COVERAGE=y)'
49 | task :rspec do
50 | require 'rspec/core/rake_task'
51 | RSpec::Core::RakeTask.new(:_specs) do |task|
52 | task.verbose = false
53 | end
54 | Rake::Task['_specs'].invoke
55 | end
56 |
57 | desc 'Run all cucumber tests (enable coverage with COVERAGE=y)'
58 | task :cucumber do
59 | require 'cucumber/rake/task'
60 |
61 | Cucumber::Rake::Task.new(:_features) do |task|
62 | task.cucumber_opts = '--quiet --format progress --require features features'
63 | end
64 | Rake::Task['_features'].invoke
65 | end
66 |
67 | end
68 |
--------------------------------------------------------------------------------
/cucumber.yml:
--------------------------------------------------------------------------------
1 | default: --format progress --tags ~@pending
2 | pending: --format progress --tags @pending --dry-run
--------------------------------------------------------------------------------
/dummy_box/Cucumber_Vagrantfile:
--------------------------------------------------------------------------------
1 | Vagrant.configure('2') do |config|
2 | config.vm.provider :proxmox do |proxmox|
3 | proxmox.endpoint = 'https://proxmox.example.com/api2/json'
4 | proxmox.user_name = 'vagrant'
5 | proxmox.password = 'password'
6 | proxmox.vm_type = :openvz
7 | proxmox.openvz_os_template = 'local:vztmpl/template.tar.gz'
8 | proxmox.vm_id_range = 900..910
9 | proxmox.vm_name_prefix = 'vagrant_test_'
10 | proxmox.vm_memory = 256
11 | proxmox.task_timeout = 30
12 | proxmox.task_status_check_interval = 1
13 | end
14 |
15 | config.vm.define :machine, primary: true do |machine|
16 | machine.vm.box = 'b681e2bc-617b-4b35-94fa-edc92e1071b8'
17 | machine.vm.network :public_network, ip: '172.16.100.1'
18 | machine.vm.provision 'shell', path: 'dummy_box/provision.sh'
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/dummy_box/Vagrantfile:
--------------------------------------------------------------------------------
1 | Vagrant.configure('2') do |config|
2 | config.vm.provider :proxmox do |proxmox|
3 | proxmox.endpoint = 'https://proxmox.example.com/api2/json'
4 | proxmox.user_name = 'vagrant'
5 | proxmox.password = 'password'
6 | proxmox.vm_type = :openvz
7 | proxmox.openvz_os_template = 'local:vztmpl/template.tar.gz'
8 | proxmox.vm_id_range = 900..910
9 | proxmox.vm_name_prefix = 'vagrant_test_'
10 | proxmox.vm_memory = 256
11 | end
12 |
13 | config.vm.define :machine, primary: true do |machine|
14 | machine.vm.box = 'b681e2bc-617b-4b35-94fa-edc92e1071b8'
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/dummy_box/Vagrantfile_qemu:
--------------------------------------------------------------------------------
1 | Vagrant.configure('2') do |config|
2 | config.vm.provider :proxmox do |proxmox|
3 | proxmox.endpoint = 'https://proxmox.example.com/api2/json'
4 | proxmox.user_name = 'vagrant'
5 | proxmox.password = 'password'
6 | proxmox.vm_type = :qemu
7 | proxmox.qemu_os = :l26
8 | proxmox.qemu_iso_file = './tmp/isofile.iso'
9 | proxmox.qemu_disk_size = '30G'
10 | proxmox.vm_id_range = 900..910
11 | proxmox.vm_name_prefix = 'vagrant_test_'
12 | proxmox.vm_memory = 256
13 | end
14 |
15 | config.vm.define :machine, primary: true do |machine|
16 | machine.vm.box = 'b681e2bc-617b-4b35-94fa-edc92e1071b8'
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/dummy_box/dummy.box:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/telcat/vagrant-proxmox/59dd4b55ff1ecf59bd1eda5bb65e8f871124b904/dummy_box/dummy.box
--------------------------------------------------------------------------------
/dummy_box/metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "provider": "proxmox"
3 | }
4 |
--------------------------------------------------------------------------------
/dummy_box/provision.sh:
--------------------------------------------------------------------------------
1 | #!bin/sh
2 |
--------------------------------------------------------------------------------
/features/destroy_command.feature:
--------------------------------------------------------------------------------
1 | Feature: VM Destruction
2 | As a system administrator I want to destroy a virtual machine.
3 |
4 | Scenario: A running virtual machine exists on the proxmox server and the user confirms the destruction
5 | Given a proxmox virtual machine exists
6 | And it is running
7 | And I run "vagrant destroy" and answer the confirmation with "Y"
8 | Then the machine should not exist any longer
9 | And I should see "Shutting down the virtual machine..."
10 | And I should see "Destroying the virtual machine..."
11 |
12 | Scenario: A running virtual machine exists on the proxmox server and the user does not confirm the destruction
13 | Given a proxmox virtual machine exists
14 | And it is running
15 | When I run "vagrant destroy" and answer the confirmation with "n"
16 | Then the machine should still exist
17 | And it is still running
18 |
19 | Scenario: A stopped virtual machine exists on the proxmox server and the user confirms the destruction
20 | Given a proxmox virtual machine exists
21 | And it is stopped
22 | When I run "vagrant destroy" and answer the confirmation with "Y"
23 | Then the machine should not exist any longer
24 | And I should see "Destroying the virtual machine..."
25 |
26 | Scenario: A stopped virtual machine exists on the proxmox server and the user does not confirm the destruction
27 | Given a proxmox virtual machine exists
28 | And it is stopped
29 | When I run "vagrant destroy" and answer the confirmation with "n"
30 | Then the machine should still exist
31 |
32 | Scenario: The virtual machine does not exist on the proxmox server
33 | Given no proxmox virtual machine exists
34 | When I run "vagrant destroy"
35 | Then I should see "The virtual machine is not created on the server!"
36 |
--------------------------------------------------------------------------------
/features/halt_command.feature:
--------------------------------------------------------------------------------
1 | Feature: VM Shutdown
2 | As a system administrator I want to shut a virtual machine down (halt).
3 |
4 | Scenario: A running virtual machine exists on the proxmox server
5 | Given a proxmox virtual machine exists
6 | And it is running
7 | When I run "vagrant halt"
8 | Then the machine is no longer running
9 | And I should see "Shutting down the virtual machine..."
10 |
11 | Scenario: A stopped virtual machine exists on the proxmox server
12 | Given a proxmox virtual machine exists
13 | And it is stopped
14 | When I run "vagrant halt"
15 | Then I should see "The virtual machine is already stopped"
16 |
17 | Scenario: The virtual machine does not exist on the proxmox server
18 | Given no proxmox virtual machine exists
19 | When I run "vagrant halt"
20 | Then I should see "The virtual machine is not created on the server!"
--------------------------------------------------------------------------------
/features/provision_command.feature:
--------------------------------------------------------------------------------
1 | Feature: VM Provisioning
2 | As a system administrator I want to provision a virtual machine on the proxmox server so that its
3 | deployment state matches its configuraton.
4 |
5 | Scenario: The virtual machine exists on the proxmox server
6 | Given a proxmox virtual machine exists
7 | And it is running
8 | When I run "vagrant provision"
9 | Then Vagrant provisions the virtual machine
10 | And the local project folder is synchronized with the virtual machine
11 |
12 | Scenario: A stopped virtual machine exists on the proxmox server
13 | Given a proxmox virtual machine exists
14 | And it is stopped
15 | When I run "vagrant provision"
16 | Then I should see "VM must be running to execute this command."
17 |
18 | Scenario: The virtual machine does not exist on the proxmox server
19 | Given no proxmox virtual machine exists
20 | When I run "vagrant provision"
21 | Then I should see "The virtual machine is not created on the server"
22 |
--------------------------------------------------------------------------------
/features/replace_iso_file.feature:
--------------------------------------------------------------------------------
1 | Feature: Use new iso file
2 | As a system administrator I want to choose an iso file to be uploaded to the proxmox server in order to
3 | use its contents for the virtual machine. It should replace the already existing file of the same name.
4 |
5 | Background:
6 | Given a Vagrantfile with these provider settings:
7 | """
8 | Vagrant.configure('2') do |config|
9 | config.vm.provider :proxmox do |proxmox|
10 | proxmox.endpoint = 'https://proxmox.example.com/api2/json'
11 | proxmox.user_name = 'vagrant'
12 | proxmox.password = 'password'
13 | proxmox.vm_type = :qemu
14 | proxmox.qemu_os = :l26
15 | proxmox.qemu_iso_file = './tmp/justanisofile.iso'
16 | proxmox.replace_qemu_iso_file = true
17 | proxmox.qemu_disk_size = '30G'
18 | end
19 | config.vm.define :machine, primary: true do |machine|
20 | machine.vm.box = 'b681e2bc-617b-4b35-94fa-edc92e1071b8'
21 | machine.vm.network :public_network, ip: '172.16.100.1', macaddress: 'aa:bb:cc:dd:ee:ff'
22 | end
23 | end
24 | """
25 |
26 | Scenario: An iso file is specified in the Vagrantfile and does not exist on the proxmox server
27 | Given An iso file "./tmp/justanisofile.iso" exists locally
28 | When I run "vagrant up --provider=proxmox --no-provision"
29 | Then The iso file "./tmp/justanisofile.iso" is uploaded into the local storage of the vm node
30 | And the new virtual machine using the iso "local:iso/justanisofile.iso" is created
31 |
32 | Scenario: An iso file is specified in the Vagrantfile and already exists on the proxmox server
33 | Given An iso file "./tmp/justanisofile.iso" exists locally
34 | And the iso file "justanisofile.iso" already exists in the proxmox storage
35 | When I run "vagrant up --provider=proxmox --no-provision"
36 | Then The iso file "justanisofile.iso" is deleted from proxmox
37 | And The iso file "./tmp/justanisofile.iso" is uploaded into the local storage of the vm node
38 | And the new virtual machine using the iso "local:iso/justanisofile.iso" is created
39 |
40 | Scenario: An iso file is specified in the Vagrantfile but does not exist locally
41 | When I run "vagrant up --provider=proxmox --no-provision"
42 | Then I should see "File for upload not found"
43 |
44 | Scenario: An iso file is specified in the Vagrantfile and an error occurs during upload
45 | Given An iso file "./tmp/justanisofile.iso" exists locally
46 | But during upload an error will occur
47 | When I run "vagrant up --provider=proxmox --no-provision"
48 | Then I should see "Error during upload"
49 |
--------------------------------------------------------------------------------
/features/replace_template_file.feature:
--------------------------------------------------------------------------------
1 | Feature: Use new template file
2 | As a system administrator I want to choose a template file to be uploaded to the proxmox server in order to
3 | generate the filesystem of the virtual machine.
4 |
5 | Background:
6 | Given a Vagrantfile with these provider settings:
7 | """
8 | Vagrant.configure('2') do |config|
9 | config.vm.provider :proxmox do |proxmox|
10 | proxmox.endpoint = 'https://proxmox.example.com/api2/json'
11 | proxmox.user_name = 'vagrant'
12 | proxmox.password = 'password'
13 | proxmox.vm_type = :openvz
14 | proxmox.openvz_template_file = './tmp/mytemplate.tar.gz'
15 | proxmox.replace_openvz_template_file = true
16 | end
17 | config.vm.define :machine, primary: true do |machine|
18 | machine.vm.box = 'b681e2bc-617b-4b35-94fa-edc92e1071b8'
19 | machine.vm.network :public_network, ip: '172.16.100.1'
20 | end
21 | end
22 | """
23 |
24 | Scenario: A template is specified in the Vagrantfile and does not exist on the proxmox server
25 | Given A templatefile "./tmp/mytemplate.tar.gz" exists locally
26 | When I run "vagrant up --provider=proxmox --no-provision"
27 | Then The template file "./tmp/mytemplate.tar.gz" is uploaded into the local storage of the vm node
28 | And the new virtual machine using the template "local:vztmpl/mytemplate.tar.gz" is created
29 |
30 | Scenario: A template is specified in the Vagrantfile and already exists on the proxmox server
31 | Given A templatefile "./tmp/mytemplate.tar.gz" exists locally
32 | And the template file "mytemplate.tar.gz" already exists in the proxmox storage
33 | When I run "vagrant up --provider=proxmox --no-provision"
34 | Then The template file "mytemplate.tar.gz" is deleted from proxmox
35 | And the new virtual machine using the template "local:vztmpl/mytemplate.tar.gz" is created
36 |
37 | Scenario: A template is specified in the Vagrantfile but does not exist locally
38 | When I run "vagrant up --provider=proxmox --no-provision"
39 | Then I should see "File for upload not found"
40 |
41 | Scenario: A template is specified in the Vagrantfile and an error occurs during upload
42 | Given A templatefile "./tmp/mytemplate.tar.gz" exists locally
43 | But during upload an error will occur
44 | When I run "vagrant up --provider=proxmox --no-provision"
45 | Then I should see "Error during upload"
--------------------------------------------------------------------------------
/features/ssh_command.feature:
--------------------------------------------------------------------------------
1 | Feature: VM ssh
2 | As a system administrator I want use ssh with the virtual machine
3 |
4 | Scenario: A running virtual machine exists on the proxmox server
5 | Given a proxmox virtual machine exists
6 | And it is running
7 | When I run "vagrant ssh"
8 | Then an ssh shell should be provided
9 |
10 | Scenario: A stopped virtual machine exists on the proxmox server
11 | Given a proxmox virtual machine exists
12 | And it is stopped
13 | When I run "vagrant ssh"
14 | Then I should see "VM must be running to execute this command."
15 |
16 | Scenario: The virtual machine does not exist on the proxmox server
17 | Given no proxmox virtual machine exists
18 | When I run "vagrant ssh"
19 | Then I should see "The virtual machine is not created on the server!"
--------------------------------------------------------------------------------
/features/ssh_run_command.feature:
--------------------------------------------------------------------------------
1 | Feature: VM ssh
2 | As a system administrator I want use execute remote command with ssh on the virtual machine
3 |
4 | Scenario: A running virtual machine exists on the proxmox server
5 | Given a proxmox virtual machine exists
6 | And it is running
7 | When I run "vagrant ssh --command foo"
8 | Then the "foo" command is executed using ssh
9 |
10 | Scenario: A stopped virtual machine exists on the proxmox server
11 | Given a proxmox virtual machine exists
12 | And it is stopped
13 | When I run "vagrant ssh --command foo"
14 | Then I should see "VM must be running to execute this command."
15 |
16 | Scenario: The virtual machine does not exist on the proxmox server
17 | Given no proxmox virtual machine exists
18 | When I run "vagrant ssh --command foo"
19 | Then I should see "The virtual machine is not created on the server!"
20 |
--------------------------------------------------------------------------------
/features/status_command.feature:
--------------------------------------------------------------------------------
1 | Feature: VM Status
2 | As a system administrator I want check the status of a virtual machine.
3 |
4 | Scenario: A running virtual machine exists on the proxmox server
5 | Given a proxmox virtual machine exists
6 | And it is running
7 | When I run "vagrant status"
8 | Then I should see "running"
9 |
10 | Scenario: A stopped virtual machine exists on the proxmox server
11 | Given a proxmox virtual machine exists
12 | And it is stopped
13 | When I run "vagrant status"
14 | Then I should see "stopped"
15 |
16 | Scenario: The virtual machine does not exist on the proxmox server
17 | Given no proxmox virtual machine exists
18 | When I run "vagrant status"
19 | Then I should see "not created"
20 |
--------------------------------------------------------------------------------
/features/step_definitions/general_steps.rb:
--------------------------------------------------------------------------------
1 | Given(/^a proxmox virtual machine exists$/) do
2 | up_machine
3 | end
4 |
5 | Given(/^no proxmox virtual machine exists$/) do
6 | up_machine
7 | stub_request(:get, proxmox_api_url('/nodes/node1/openvz/900/status/current')).
8 | to_return(status: 500)
9 | end
10 |
11 | When(/^I run "vagrant (\w+)(?:\s)?([^"]*)?"$/) do |command, params|
12 |
13 | execute_vagrant_command command.to_sym, *(params.split)
14 | end
15 |
16 | When(/^I run "vagrant (\w+)(?:\s)?([^"]*)?" and answer the confirmation with "(\w+)"$/) do |command, params, answer|
17 | stub_ui_input answer
18 | execute_vagrant_command command.to_sym, *(params.split)
19 | end
20 |
21 | And(/^it is running$/) do
22 | stub_request(:get, proxmox_api_url('/nodes/node1/openvz/900/status/current')).
23 | to_return(body: {data: {status: 'running'}}.to_json)
24 | end
25 |
26 | And(/^it is stopped$/) do
27 | stub_request(:get, proxmox_api_url('/nodes/node1/openvz/900/status/current')).
28 | to_return(body: {data: {status: 'stopped'}}.to_json)
29 | end
30 |
31 | Then(/^I should see "([^"]*)"$/) do |text|
32 | expect_vagrant_ui_message /#{text}/
33 | end
34 |
35 | Then(/^the machine should not exist any longer$/) do
36 | assert_requested :delete, proxmox_api_url('/nodes/node1/openvz/900')
37 | end
38 |
39 | Then(/^the machine should still exist$/) do
40 | assert_not_requested :delete, proxmox_api_url('/nodes/node1/openvz/900')
41 | end
42 |
43 | And(/^it is still running$/) do
44 | assert_not_requested :post, proxmox_api_url('/nodes/node1/openvz/900/status/shutdown')
45 | end
46 |
47 | Then(/^the machine is no longer running$/) do
48 | assert_requested :post, proxmox_api_url('/nodes/node1/openvz/900/status/shutdown')
49 | end
50 |
51 | Then(/^the machine is now running$/) do
52 | assert_requested :post, proxmox_api_url('/nodes/node1/openvz/900/status/start')
53 | end
54 |
55 | Then(/^Vagrant provisions the virtual machine$/) do
56 | expect_remote_vagrant_call /\/tmp\/vagrant-shell/
57 | end
58 |
59 | Then(/^the local project folder is synchronized with the virtual machine$/) do
60 | expect_local_vagrant_call /rsync .+ #{Dir.pwd}\/ vagrant@172.16.100.1:\/vagrant/
61 | end
62 |
63 | Then(/^an ssh shell should be provided$/) do
64 | expect_vagrant_ssh_command ""
65 | end
66 |
67 | Then(/^the "([^"]*)" command is executed using ssh$/) do |command|
68 | expect_vagrant_ssh_command /#{command}/
69 | end
70 |
71 | Given(/^a Vagrantfile with these provider settings:$/) do |settings|
72 | prepare_and_stub_custom_environment settings
73 | end
74 |
75 | Then(/^the new virtual machine using the template "([^"]*)" is created$/) do |template|
76 | assert_requested :post, proxmox_api_url('/nodes/node1/openvz'), body: /#{CGI.escape(template)}/
77 | end
78 |
79 | Then(/^The template file "([^"]*)" is uploaded into the local storage of the vm node$/) do |filename|
80 | assert_requested :post, proxmox_api_url('/nodes/node1/storage/local/upload')
81 | end
82 |
83 | Then(/^The template file "([^"]*)" is not uploaded$/) do |filename|
84 | assert_not_requested :post, proxmox_api_url('/nodes/node1/storage/local/upload')
85 | end
86 |
87 | Given(/^the template file "([^"]*)" already exists in the proxmox storage$/) do |template|
88 | remove_request_stub @storage_content_request_stub
89 | @storage_content_request_stub = stub_request(:get, proxmox_api_url('/nodes/node1/storage/local/content')).
90 | to_return(body: {data: [{volid: "local:vztmpl/#{template}"}]}.to_json)
91 | end
92 |
93 | Given(/^A templatefile "([^"]*)" exists locally$/) do |filename|
94 | touch_tempfile filename
95 | end
96 |
97 | But(/^during upload an error will occur$/) do
98 | stub_request(:post, proxmox_api_url('/nodes/node1/storage/local/upload')).
99 | to_return status: 500
100 | end
101 |
102 | Then(/^(\d+) seconds should have passed$/) do |interval|
103 | expect(Time).to have_elapsed interval.to_i.seconds
104 | end
105 |
106 | And(/^it won't response to ssh once it's started$/) do
107 | CommunicatorMock.ssh_enabled = false
108 | end
109 |
110 | Given(/^An iso file "([^"]*)" exists locally$/) do |filename|
111 | touch_tempfile filename
112 | end
113 |
114 | Then(/^The iso file "([^"]*)" is uploaded into the local storage of the vm node$/) do |_|
115 | assert_requested :post, proxmox_api_url('/nodes/node1/storage/local/upload')
116 | end
117 |
118 | And(/^the new virtual machine using the iso "([^"]*)" is created$/) do |iso|
119 | assert_requested :post, proxmox_api_url('/nodes/node1/qemu'), body: /#{CGI.escape(iso)}/
120 | end
121 |
122 | And(/^the iso file "([^"]*)" already exists in the proxmox storage$/) do |iso|
123 | remove_request_stub @storage_content_request_stub
124 | @storage_content_request_stub = stub_request(:get, proxmox_api_url('/nodes/node1/storage/local/content')).
125 | to_return(body: {data: [{volid: "local:iso/#{iso}"}]}.to_json)
126 | end
127 |
128 | Then(/^The iso file "([^"]*)" is not uploaded$/) do |_|
129 | assert_not_requested :post, proxmox_api_url('/nodes/node1/storage/local/upload')
130 | end
131 |
132 | Then(/^The iso file "([^"]*)" is deleted from proxmox$/) do |iso|
133 | assert_requested :delete, proxmox_api_url("/nodes/node1/storage/local/content/iso/#{iso}")
134 | end
135 |
136 | Then(/^The template file "([^"]*)" is deleted from proxmox$/) do |template|
137 | assert_requested :delete, proxmox_api_url("/nodes/node1/storage/local/content/vztmpl/#{template}")
138 | end
--------------------------------------------------------------------------------
/features/support/env.rb:
--------------------------------------------------------------------------------
1 | require 'vagrant-proxmox'
2 | require 'cucumber/rspec/doubles'
3 | require 'rspec/matchers'
4 | require 'active_support/core_ext/string'
5 | require 'webmock/cucumber'
6 | require_relative 'vagrant_process_mock'
7 | require_relative 'machine_helper'
8 | require_relative 'template_helper'
9 | require_relative 'file_helper'
10 |
11 | ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__) + '/../../bin')}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
12 | LIB_DIR = File.join(File.expand_path(File.dirname(__FILE__)), '..', '..', 'lib')
13 |
14 | add_dummy_box
15 |
16 | at_exit do
17 | remove_dummy_box
18 | end
19 |
20 | Before do
21 | @ui = VagrantUIMock.new
22 | @original_rubylib = ENV['RUBYLIB']
23 | ENV['RUBYLIB'] = LIB_DIR + File::PATH_SEPARATOR + ENV['RUBYLIB'].to_s
24 | FileUtils.rm_r '.vagrant', force: true
25 | VagrantSSHMock.reset! self
26 | clear_tempfile_list
27 | CommunicatorMock.ssh_enabled = true
28 | end
29 |
30 | After do
31 | ENV['RUBYLIB'] = @original_rubylib
32 | remove_all_tempfiles
33 | end
34 |
--------------------------------------------------------------------------------
/features/support/file_helper.rb:
--------------------------------------------------------------------------------
1 | def touch_tempfile filename
2 | FileUtils.mkdir_p File.dirname filename
3 | FileUtils.touch (filename)
4 | @step_tempfiles << filename
5 | end
6 |
7 | def remove_all_tempfiles
8 | @step_tempfiles.each do |tempfile|
9 | File.unlink tempfile
10 | end
11 | end
12 |
13 | def clear_tempfile_list
14 | @step_tempfiles = []
15 | end
--------------------------------------------------------------------------------
/features/support/machine_helper.rb:
--------------------------------------------------------------------------------
1 | def stub_machine_initialization
2 | stub_remote_vagrant_call /mkdir/
3 | stub_remote_vagrant_call /chown/
4 | stub_local_vagrant_call /rsync/
5 | stub_remote_vagrant_call /chmod/
6 | stub_request(:post, proxmox_api_url('/access/ticket')).
7 | to_return body: {data: {ticket: 'ticket', CSRFPreventionToken: 'token'}}.to_json
8 | stub_request(:get, proxmox_api_url('/nodes')).
9 | to_return body: {data: [{node: 'node1'}]}.to_json
10 | stub_request(:get, proxmox_api_url('/cluster/resources?type=vm')).
11 | to_return body: {data: [{node: 'node1', id: 'openvz/900'}]}.to_json
12 | stub_request(:post, proxmox_api_url('/nodes/node1/openvz')).
13 | to_return body: {data: 'UPID:node1:A:B:C:D:task_type:user@host:'}.to_json
14 | stub_request(:get, proxmox_api_url('/nodes/node1/tasks/UPID:node1:A:B:C:D:task_type:user@host:/status')).
15 | to_return body: {data: {exitstatus: 'OK'}}.to_json
16 | stub_request(:post, proxmox_api_url('/nodes/node1/openvz/900/status/start')).
17 | to_return body: {data: 'UPID:node1:A:B:C:D:task_type:user@host:'}.to_json
18 | stub_request(:get, proxmox_api_url('/nodes/node1/openvz/900/status/current')).
19 | to_return(body: {data: {status: 'running'}}.to_json)
20 | @storage_content_request_stub = stub_request(:get, proxmox_api_url('/nodes/node1/storage/local/content')).
21 | to_return body: {data: [{node: 'node1', id: 'openvz/900'}]}.to_json
22 | stub_request(:post, proxmox_api_url('/nodes/node1/storage/local/upload')).
23 | to_return body: {data: 'UPID:node1:A:B:C:D:task_type:user@host:'}.to_json
24 | stub_request(:delete, proxmox_api_url('/nodes/node1/storage/local/content/iso/justanisofile.iso')).
25 | to_return do |request|
26 | remove_request_stub @storage_content_request_stub
27 | @storage_content_request_stub = stub_request(:get, proxmox_api_url('/nodes/node1/storage/local/content')).
28 | to_return(body: {data: []}.to_json)
29 | {body: {data: nil}.to_json}
30 | end
31 | stub_request(:delete, proxmox_api_url('/nodes/node1/storage/local/content/vztmpl/mytemplate.tar.gz')).
32 | to_return do |request|
33 | remove_request_stub @storage_content_request_stub
34 | @storage_content_request_stub = stub_request(:get, proxmox_api_url('/nodes/node1/storage/local/content')).
35 | to_return(body: {data: []}.to_json)
36 | {body: {data: nil}.to_json}
37 | end
38 | end
39 |
40 | def stub_default_calls
41 | @ui.reset!
42 | WebMock.reset!
43 | VagrantProcessMock.reset_history!
44 | stub_request(:post, proxmox_api_url('/access/ticket')).
45 | to_return body: {data: {ticket: 'ticket', CSRFPreventionToken: 'token'}}.to_json
46 | stub_request(:get, proxmox_api_url('/nodes')).
47 | to_return body: {data: [{node: 'node1'}]}.to_json
48 | stub_request(:get, proxmox_api_url('/cluster/resources?type=vm')).
49 | to_return body: {data: [{node: 'node1', id: 'openvz/900'}]}.to_json
50 | stub_request(:post, proxmox_api_url('/nodes/node1/openvz')).
51 | to_return body: {data: 'UPID:node1:A:B:C:D:task_type:user@host:'}.to_json
52 | stub_request(:get, proxmox_api_url('/nodes/node1/tasks/UPID:node1:A:B:C:D:task_type:user@host:/status')).
53 | to_return body: {data: {exitstatus: 'OK'}}.to_json
54 | stub_request(:post, proxmox_api_url('/nodes/node1/openvz/900/status/shutdown')).
55 | to_return body: {data: 'UPID:node1:A:B:C:D:task_type:user@host:'}.to_json
56 | stub_request(:delete, proxmox_api_url('/nodes/node1/openvz/900')).
57 | to_return body: {data: 'UPID:node1:A:B:C:D:task_type:user@host:'}.to_json
58 | stub_request(:post, proxmox_api_url('/nodes/node1/openvz/900/status/start')).
59 | to_return body: {data: 'UPID:node1:A:B:C:D:task_type:user@host:'}.to_json
60 | stub_request(:get, proxmox_api_url('/nodes/node1/storage/local/content')).
61 | to_return body: {data: []}.to_json
62 | stub_request(:post, proxmox_api_url('/nodes/node1/storage/local/upload')).
63 | to_return body: {data: 'UPID:node1:A:B:C:D:task_type:user@host:'}.to_json
64 | stub_request(:delete, proxmox_api_url('/nodes/node1/storage/local/content/iso/justanisofile.iso')).
65 | to_return do |request|
66 | remove_request_stub @storage_content_request_stub
67 | @storage_content_request_stub = stub_request(:get, proxmox_api_url('/nodes/node1/storage/local/content')).
68 | to_return(body: {data: []}.to_json)
69 | {body: {data: nil}.to_json}
70 | end
71 | stub_request(:delete, proxmox_api_url('/nodes/node1/storage/local/content/vztmpl/mytemplate.tar.gz')).
72 | to_return do |request|
73 | remove_request_stub @storage_content_request_stub
74 | @storage_content_request_stub = stub_request(:get, proxmox_api_url('/nodes/node1/storage/local/content')).
75 | to_return(body: {data: []}.to_json)
76 | {body: {data: nil}.to_json}
77 | end
78 | stub_request(:get, proxmox_api_url('/nodes/node1/network/vmbr0')).
79 | to_return body: {data: {}}.to_json
80 | end
81 |
82 | def up_machine
83 | stub_local_vagrant_call 'ps -o comm= 1'
84 | @environment = Vagrant::Environment.new vagrantfile_name: 'dummy_box/Cucumber_Vagrantfile'
85 | @environment.instance_variable_set :@ui, @ui
86 | stub_machine_initialization
87 | execute_vagrant_command :up, '--provider=proxmox', '--no-provision'
88 | stub_default_calls
89 | end
90 |
91 | def proxmox_api_url path
92 | "https://proxmox.example.com/api2/json#{path}"
93 | end
94 |
95 | def execute_vagrant_command command, *params
96 | begin
97 | Vagrant.plugin('2').manager.commands[command].new(params, @environment).execute
98 | rescue => e
99 | @ui.error e.to_s
100 | end
101 | end
102 |
103 | def add_dummy_box
104 | begin
105 | VagrantProcessMock.enabled = false
106 | Vagrant::Environment.new.boxes.add 'dummy_box/dummy.box', 'b681e2bc-617b-4b35-94fa-edc92e1071b8', :proxmox
107 | VagrantProcessMock.enabled = true
108 | rescue Vagrant::Errors::BoxAlreadyExists
109 | end
110 | end
111 |
112 | def remove_dummy_box
113 | stub_local_vagrant_call 'ps -o comm= 1'
114 | @environment = Vagrant::Environment.new vagrantfile_name: 'dummy_box/Cucumber_Vagrantfile'
115 | execute_vagrant_command :box, 'remove', 'b681e2bc-617b-4b35-94fa-edc92e1071b8'
116 | end
117 |
--------------------------------------------------------------------------------
/features/support/template_helper.rb:
--------------------------------------------------------------------------------
1 | require 'erb'
2 |
3 | def create_vagrantfile content
4 | vagrantfile = Tempfile.new 'vagrantfile'
5 | vagrantfile.write content
6 | vagrantfile.flush
7 | vagrantfile
8 | end
9 |
10 | def prepare_and_stub_custom_environment settings
11 | stub_local_vagrant_call 'ps -o comm= 1'
12 | vagrantfile = create_vagrantfile settings
13 | @environment = Vagrant::Environment.new vagrantfile_name: vagrantfile.path
14 | @environment.instance_variable_set :@ui, @ui
15 | stub_machine_initialization
16 | end
17 |
--------------------------------------------------------------------------------
/features/support/time_helper.rb:
--------------------------------------------------------------------------------
1 | require_relative '../../spec/spec_helpers/time_helpers.rb'
2 |
3 | Before '@timecop' do
4 | Timecop.freeze
5 | allow_any_instance_of(VagrantPlugins::Proxmox::Action::ProxmoxAction).to(receive(:sleep)) { |_, duration| Timecop.travel(Time.now + duration) }
6 | end
7 |
8 | After '@timecop' do
9 | Timecop.return
10 | end
11 |
--------------------------------------------------------------------------------
/features/support/vagrant_process_mock.rb:
--------------------------------------------------------------------------------
1 | require 'vagrant/util/subprocess'
2 | require 'ostruct'
3 |
4 | module Vagrant
5 | module Util
6 | class Subprocess
7 | def self.execute *command, &block
8 | if VagrantProcessMock.enabled
9 | VagrantProcessMock.check_call command.join(' '), :local
10 | OpenStruct.new exit_code: 0, stdout: ''
11 | else
12 | new(*command).execute(&block)
13 | end
14 | end
15 | end
16 | end
17 | end
18 |
19 | class CommunicatorMock
20 |
21 | @ssh_enabled = true
22 |
23 | class << self
24 | attr_accessor :ssh_enabled
25 | end
26 |
27 | def sudo command, opts=nil, &block
28 | VagrantProcessMock.check_call command, :remote
29 | end
30 |
31 | def upload *_
32 | end
33 |
34 | def execute command, opts=nil, &block
35 | VagrantProcessMock.check_call command, :remote
36 | end
37 |
38 | def ready?
39 | CommunicatorMock.ssh_enabled
40 | end
41 | end
42 |
43 | module Vagrant
44 | class Machine
45 | def communicate
46 | CommunicatorMock.new
47 | end
48 | end
49 | end
50 |
51 | module VagrantProcessMock
52 |
53 | class << self
54 |
55 | attr_accessor :enabled
56 | attr_accessor :logging
57 |
58 | def initialize
59 | reset!
60 | @enabled = true
61 | @logging = false
62 | end
63 |
64 | def reset!
65 | @stubbed_calls = {local: [], remote: []}
66 | reset_history!
67 | end
68 |
69 | def reset_history!
70 | @calls = {local: [], remote: []}
71 | end
72 |
73 | def check_call call, type
74 | if @logging
75 | puts "Vagrant #{type} call: #{call}"
76 | end
77 | @calls[type] << call
78 | unless @stubbed_calls[type].find { |s| s === call }
79 | raise unstubbed_call_error_message call, type
80 | end
81 | end
82 |
83 | def stub_call call, type
84 | @stubbed_calls[type] << call
85 | end
86 |
87 | def expect_call call, type
88 | unless @calls[type].find { |c| call === c }
89 | raise expected_call_not_called_error_message call, type
90 | end
91 | end
92 |
93 | def expected_call_not_called_error_message call, type
94 | < e
25 | raise VagrantPlugins::Proxmox::Errors::VMCloneError, proxmox_exit_status: e.message
26 | end
27 |
28 | begin
29 | vm_id = connection(env).get_free_vm_id
30 | params = create_params_qemu(config, env, vm_id, template_vm_id)
31 | exit_status = connection(env).clone_vm node: node, vm_type: config.vm_type, params: params
32 | exit_status == 'OK' ? exit_status : raise(VagrantPlugins::Proxmox::Errors::ProxmoxTaskFailed, proxmox_exit_status: exit_status)
33 | rescue StandardError => e
34 | raise VagrantPlugins::Proxmox::Errors::VMCloneError, proxmox_exit_status: e.message
35 | end
36 |
37 | env[:machine].id = "#{node}/#{vm_id}"
38 |
39 | env[:ui].info I18n.t('vagrant_proxmox.done')
40 | next_action env
41 | end
42 |
43 | private
44 | def create_params_qemu(config, env, vm_id, template_vm_id)
45 | # without network, which will added in ConfigClonedVm
46 | {vmid: template_vm_id,
47 | newid: vm_id,
48 | name: env[:machine].config.vm.hostname || env[:machine].name.to_s,
49 | description: "#{config.vm_name_prefix}#{env[:machine].name}"}
50 | end
51 |
52 | end
53 | end
54 | end
55 | end
56 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/action/config_clone.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 | module Action
4 |
5 | # This action modifies the configuration of a cloned vm
6 | # Basically it creates a user network interface with hostfwd for the provisioning
7 | # and an interface for every public or private interface defined in the Vagrantfile
8 | class ConfigClone < ProxmoxAction
9 |
10 | def initialize app, env
11 | @app = app
12 | @logger = Log4r::Logger.new 'vagrant_proxmox::action::config_clone'
13 | @node_ip = nil
14 | @guest_port = nil
15 | end
16 |
17 | def call env
18 | env[:ui].info I18n.t('vagrant_proxmox.configuring_vm')
19 | config = env[:machine].provider_config
20 | node = env[:proxmox_selected_node]
21 | vm_id = nil
22 |
23 | begin
24 | vm_id = env[:machine].id.split("/").last
25 | @node_ip = connection(env).get_node_ip(node, 'vmbr0') if config.vm_type == :qemu
26 | @guest_port = sprintf("22%03d", vm_id.to_i).to_s
27 | rescue StandardError => e
28 | raise VagrantPlugins::Proxmox::Errors::VMConfigError, proxmox_exit_status: e.message
29 | end
30 |
31 | begin
32 | template_config = connection(env).get_vm_config node: node, vm_id: vm_id, vm_type: config.vm_type
33 | params = create_params_qemu(config, env, vm_id, template_config)
34 | exit_status = connection(env).config_clone node: node, vm_type: config.vm_type, params: params
35 | exit_status == 'OK' ? exit_status : raise(VagrantPlugins::Proxmox::Errors::ProxmoxTaskFailed, proxmox_exit_status: exit_status)
36 | rescue StandardError => e
37 | raise VagrantPlugins::Proxmox::Errors::VMConfigError, proxmox_exit_status: e.message
38 | end
39 |
40 | env[:ui].info I18n.t('vagrant_proxmox.done')
41 | next_action env
42 | end
43 |
44 | private
45 | def create_params_qemu(provider_config, env, vm_id, template_config)
46 | vm_config = env[:machine].config.vm
47 | params = {
48 | vmid: vm_id,
49 | description: "#{provider_config.vm_name_prefix}#{env[:machine].name}",
50 | }
51 | # delete existing network interfaces from template
52 | to_delete = template_config.keys.select{|key| key.to_s.match(/^net/) }
53 | params[:delete] = to_delete.join(",") if not to_delete.empty?
54 | # net0 is the provisioning network, derived from forwarded_port
55 | net_num = 0
56 | hostname = vm_config.hostname || env[:machine].name
57 | netdev0 = [
58 | "type=user",
59 | "id=net0",
60 | "hostname=#{hostname}",
61 | "hostfwd=tcp:#{@node_ip}:#{@guest_port}-:22", # selected_node's primary ip and port (22000 + vm_id)
62 | ]
63 | device0 = [
64 | "#{provider_config.qemu_nic_model}",
65 | "netdev=net0",
66 | "bus=pci.0",
67 | "addr=0x12", # starting point for network interfaces
68 | "id=net0",
69 | "bootindex=299"
70 | ]
71 | params[:args] = "-netdev " + netdev0.join(",") + " -device " + device0.join(",")
72 | # now add a network device for every public_network or private_network
73 | # ip addresses are ignored here, as we can't configure anything inside the qemu vm.
74 | # at least we can set the predefined mac address and a bridge
75 | net_num += 1
76 | vm_config.networks.each do |type, options|
77 | next if not type.match(/^p.*_network$/)
78 | nic = provider_config.qemu_nic_model
79 | nic += "=#{options[:macaddress]}" if options[:macaddress]
80 | nic += ",bridge=#{options[:bridge]}" if options[:bridge]
81 | net = 'net' + net_num.to_s
82 | params[net] = nic
83 | net_num += 1
84 | end
85 |
86 | # some more individual settings
87 | params[:ide2] = "#{provider_config.qemu_iso},media=cdrom" if provider_config.qemu_iso
88 | params[:sockets] = "#{provider_config.qemu_sockets}".to_i if provider_config.qemu_sockets
89 | params[:cores] = "#{provider_config.qemu_cores}".to_i if provider_config.qemu_cores
90 | params[:balloon] = "#{provider_config.vm_memory}".to_i if provider_config.vm_memory and provider_config.vm_memory < template_config[:balloon]
91 | params[:memory] = "#{provider_config.vm_memory}".to_i if provider_config.vm_memory
92 | params
93 | end
94 |
95 | end
96 | end
97 | end
98 | end
99 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/action/connect_proxmox.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 | module Action
4 |
5 | # This action connects to the Proxmox server and stores the
6 | # connection in env[:proxmox_connection]
7 | class ConnectProxmox < ProxmoxAction
8 |
9 | def initialize app, env
10 | @app = app
11 | @logger = Log4r::Logger.new 'vagrant_proxmox::action::connect_proxmox'
12 | end
13 |
14 | def call env
15 | begin
16 | config = env[:machine].provider_config
17 | connection = Connection.new config.endpoint,
18 | vm_id_range: config.vm_id_range,
19 | task_timeout: config.task_timeout,
20 | task_status_check_interval: config.task_status_check_interval,
21 | imgcopy_timeout: config.imgcopy_timeout
22 | connection.login username: config.user_name, password: config.password
23 | env[:proxmox_connection] = connection
24 | rescue => e
25 | raise Errors::CommunicationError, error_msg: e.message
26 | end
27 | next_action env
28 | end
29 |
30 | end
31 | end
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/action/create_vm.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 | module Action
4 |
5 | # This action creates a new virtual machine on the Proxmox server and
6 | # stores its node and vm_id env[:machine].id
7 | class CreateVm < ProxmoxAction
8 |
9 | def initialize app, env
10 | @app = app
11 | @logger = Log4r::Logger.new 'vagrant_proxmox::action::create_vm'
12 | end
13 |
14 | def call env
15 | env[:ui].info I18n.t('vagrant_proxmox.creating_vm')
16 | config = env[:machine].provider_config
17 |
18 | node = env[:proxmox_selected_node]
19 | vm_id = nil
20 |
21 | begin
22 | vm_id = connection(env).get_free_vm_id
23 | params = create_params_openvz(config, env, vm_id) if config.vm_type == :openvz
24 | params = create_params_lxc(config, env, vm_id) if config.vm_type == :lxc
25 | params = create_params_qemu(config, env, vm_id) if config.vm_type == :qemu
26 | exit_status = connection(env).create_vm node: node, vm_type: config.vm_type, params: params
27 | exit_status == 'OK' ? exit_status : raise(VagrantPlugins::Proxmox::Errors::ProxmoxTaskFailed, proxmox_exit_status: exit_status)
28 | rescue StandardError => e
29 | raise VagrantPlugins::Proxmox::Errors::VMCreateError, proxmox_exit_status: e.message
30 | end
31 |
32 | env[:machine].id = "#{node}/#{vm_id}"
33 |
34 | env[:ui].info I18n.t('vagrant_proxmox.done')
35 | next_action env
36 | end
37 |
38 | private
39 | def create_params_qemu(config, env, vm_id)
40 | network = "#{config.qemu_nic_model},bridge=#{config.qemu_bridge}"
41 | network = "#{config.qemu_nic_model}=#{get_machine_macaddress(env)},bridge=#{config.qemu_bridge}" if get_machine_macaddress(env)
42 | {vmid: vm_id,
43 | name: env[:machine].config.vm.hostname || env[:machine].name.to_s,
44 | ostype: config.qemu_os,
45 | ide2: "#{config.qemu_iso},media=cdrom",
46 | sata0: "#{config.qemu_storage}:#{config.qemu_disk_size},format=qcow2",
47 | sockets: config.qemu_sockets,
48 | cores: config.qemu_cores,
49 | memory: config.vm_memory,
50 | net0: network,
51 | description: "#{config.vm_name_prefix}#{env[:machine].name}"}
52 | end
53 |
54 | private
55 | def create_params_openvz(config, env, vm_id)
56 | {vmid: vm_id,
57 | ostemplate: config.openvz_os_template,
58 | hostname: env[:machine].config.vm.hostname || env[:machine].name.to_s,
59 | password: 'vagrant',
60 | memory: config.vm_memory,
61 | description: "#{config.vm_name_prefix}#{env[:machine].name}"}
62 | .tap do |params|
63 | params[:ip_address] = get_machine_ip_address(env) if get_machine_ip_address(env)
64 | end
65 | end
66 |
67 | private
68 | def create_params_lxc(config, env, vm_id)
69 | {vmid: vm_id,
70 | ostemplate: config.openvz_os_template,
71 | hostname: env[:machine].config.vm.hostname || env[:machine].name.to_s,
72 | password: 'vagrant',
73 | storage: "#{config.vm_storage}:#{config.vm_disk_size}",
74 | memory: config.vm_memory,
75 | description: "#{config.vm_name_prefix}#{env[:machine].name}"}
76 | .tap do |params|
77 | params[:net0] = "name=#{get_machine_interface_name(env)},ip=#{get_machine_ip_address(env)}/24,gw=#{get_machine_gw_ip(env)},bridge=#{get_machine_bridge_name(env)}" if get_machine_ip_address(env)
78 | end
79 | end
80 | end
81 | end
82 | end
83 | end
84 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/action/destroy_vm.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 | module Action
4 |
5 | # This action destroys the virtual machine env[:machine]
6 | class DestroyVm < ProxmoxAction
7 |
8 | def initialize app, env
9 | @app = app
10 | @logger = Log4r::Logger.new 'vagrant_proxmox::action::destroy_vm'
11 | end
12 |
13 | def call env
14 | env[:ui].info I18n.t('vagrant_proxmox.destroying_vm')
15 |
16 | begin
17 | node, vm_id = env[:machine].id.split '/'
18 | exit_status = connection(env).delete_vm vm_id
19 | exit_status == 'OK' ? exit_status : raise(VagrantPlugins::Proxmox::Errors::ProxmoxTaskFailed, proxmox_exit_status: exit_status)
20 | rescue StandardError => e
21 | raise VagrantPlugins::Proxmox::Errors::VMDestroyError, proxmox_exit_status: e.message
22 | end
23 |
24 | env[:ui].info I18n.t('vagrant_proxmox.done')
25 |
26 | next_action env
27 | end
28 |
29 | end
30 |
31 | end
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/action/get_node_list.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 | module Action
4 |
5 | # This action gets a list of all the nodes e.g. ['node1', 'node2'] of
6 | # a Proxmox server cluster and stores it under env[:proxmox_nodes]
7 | class GetNodeList < ProxmoxAction
8 |
9 | def initialize app, env
10 | @app = app
11 | end
12 |
13 | def call env
14 | begin
15 | env[:proxmox_nodes] = env[:proxmox_connection].get_node_list
16 | next_action env
17 | rescue => e
18 | raise Errors::CommunicationError, error_msg: e.message
19 | end
20 | end
21 |
22 | end
23 |
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/action/is_created.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 | module Action
4 |
5 | # set env[:result] to :is_created
6 | class IsCreated < ProxmoxAction
7 |
8 | def initialize app, env
9 | @app = app
10 | end
11 |
12 | def call env
13 | env[:result] = env[:machine].state.id != :not_created
14 | next_action env
15 | end
16 |
17 | end
18 |
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/action/is_stopped.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 | module Action
4 |
5 | # set env[:result] to :stopped
6 | class IsStopped < ProxmoxAction
7 |
8 | def initialize app, env
9 | @app = app
10 | end
11 |
12 | def call env
13 | env[:result] = env[:machine].state.id == :stopped
14 | next_action env
15 | end
16 |
17 | end
18 |
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/action/message_already_running.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 | module Action
4 |
5 | class MessageAlreadyRunning < ProxmoxAction
6 |
7 | def initialize app, env
8 | @app = app
9 | end
10 |
11 | def call env
12 | env[:ui].info I18n.t('vagrant_proxmox.already_running')
13 | next_action env
14 | end
15 |
16 | end
17 |
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/action/message_already_stopped.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 | module Action
4 |
5 | class MessageAlreadyStopped < ProxmoxAction
6 |
7 | def initialize app, env
8 | @app = app
9 | end
10 |
11 | def call env
12 | env[:ui].info I18n.t('vagrant_proxmox.already_stopped')
13 | next_action env
14 | end
15 |
16 | end
17 |
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/action/message_file_not_found.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 | module Action
4 |
5 | class MessageFileNotFound < ProxmoxAction
6 |
7 | def initialize app, env
8 | @app = app
9 | end
10 |
11 | def call env
12 | #TODO add file name
13 | env[:ui].info I18n.t('vagrant_proxmox.errors.file_not_found')
14 | next_action env
15 | end
16 |
17 | end
18 |
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/action/message_not_created.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 | module Action
4 |
5 | class MessageNotCreated < ProxmoxAction
6 |
7 | def initialize app, env
8 | @app = app
9 | end
10 |
11 | def call env
12 | env[:ui].info I18n.t('vagrant_proxmox.not_created')
13 | next_action env
14 | end
15 |
16 | end
17 |
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/action/message_not_running.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 | module Action
4 |
5 | class MessageNotRunning < ProxmoxAction
6 |
7 | def initialize app, env
8 | @app = app
9 | end
10 |
11 | def call env
12 | env[:ui].info I18n.t('vagrant_proxmox.errors.vm_not_running')
13 | next_action env
14 | end
15 |
16 | end
17 |
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/action/message_upload_server_error.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 | module Action
4 |
5 | class MessageUploadServerError < ProxmoxAction
6 |
7 | def initialize app, env
8 | @app = app
9 | end
10 |
11 | def call env
12 | env[:ui].info I18n.t('vagrant_proxmox.errors.server_upload_error')
13 | next_action env
14 | end
15 |
16 | end
17 |
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/action/proxmox_action.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 | module Action
4 |
5 | class ProxmoxAction
6 |
7 | protected
8 | def next_action env
9 | @app.call env
10 | end
11 |
12 | protected
13 | def get_machine_ip_address env
14 | config = env[:machine].provider_config
15 | if config.vm_type == :qemu
16 | env[:machine].config.vm.networks.select { |type, _| type == :forwarded_port }.first[1][:host_ip] rescue nil
17 | else
18 | env[:machine].config.vm.networks.select { |type, _| type == :public_network }.first[1][:ip] rescue nil
19 | end
20 | end
21 |
22 | protected
23 | def get_machine_interface_name env
24 | env[:machine].config.vm.networks.select { |type, _| type == :public_network }.first[1][:interface] rescue nil
25 | end
26 |
27 | protected
28 | def get_machine_bridge_name env
29 | env[:machine].config.vm.networks.select { |type, _| type == :public_network }.first[1][:bridge] rescue nil
30 | end
31 |
32 | protected
33 | def get_machine_gw_ip env
34 | env[:machine].config.vm.networks.select { |type, _| type == :public_network }.first[1][:gw] rescue nil
35 | end
36 |
37 | protected
38 | def get_machine_macaddress env
39 | env[:machine].config.vm.networks.select { |type, _| type == :public_network }.first[1][:macaddress] rescue nil
40 | end
41 |
42 | protected
43 | def connection env
44 | env[:proxmox_connection]
45 | end
46 |
47 | end
48 |
49 | end
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/action/read_ssh_info.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 | module Action
4 |
5 | # This action stores the ssh information in env[:machine_ssh_info]
6 | class ReadSSHInfo < ProxmoxAction
7 |
8 | def initialize app, env
9 | @app = app
10 | @logger = Log4r::Logger.new 'vagrant_proxmox::action::read_ssh_info'
11 | end
12 |
13 | def call env
14 | env[:machine_ssh_info] = get_machine_ip_address(env).try do |ip_address|
15 | {host: ip_address, port: env[:machine].config.ssh.guest_port}
16 | end
17 | env[:machine_ssh_info]
18 | next_action env
19 | end
20 |
21 | end
22 |
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/action/read_state.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 | module Action
4 |
5 | # This action reads the state of a Proxmox virtual machine and stores it
6 | # in env[:machine_state_id].
7 | class ReadState < ProxmoxAction
8 |
9 | def initialize app, env
10 | @app = app
11 | @logger = Log4r::Logger.new 'vagrant_proxmox::action::read_state'
12 | end
13 |
14 | def call env
15 | begin
16 | env[:machine_state_id] =
17 | if env[:machine].id
18 | node, vm_id = env[:machine].id.split '/'
19 | env[:proxmox_connection].get_vm_state vm_id
20 | else
21 | :not_created
22 | end
23 | next_action env
24 | rescue => e
25 | raise Errors::CommunicationError, error_msg: e.message
26 | end
27 |
28 | end
29 |
30 | end
31 |
32 | end
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/action/select_node.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 | module Action
4 |
5 | # This action reads the state of a Proxmox virtual machine and stores it
6 | # in env[:machine_state_id].
7 | class SelectNode < ProxmoxAction
8 |
9 | def initialize app, env
10 | @app = app
11 | @logger = Log4r::Logger.new 'vagrant_proxmox::action::select_node'
12 | end
13 |
14 | def call env
15 | if env[:machine].provider_config.selected_node != Config::UNSET_VALUE
16 | if env[:proxmox_nodes].include?(env[:machine].provider_config.selected_node)
17 | env[:proxmox_selected_node] = env[:machine].provider_config.selected_node
18 | else
19 | raise Errors::InvalidNodeError, node: env[:machine].provider_config.selected_node
20 | end
21 | else
22 | env[:proxmox_selected_node] = env[:proxmox_nodes].sample
23 | end
24 | next_action env
25 | end
26 |
27 | end
28 |
29 | end
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/action/shutdown_vm.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 | module Action
4 |
5 | # This action shuts down the Proxmox virtual machine in env[:machine]
6 | class ShutdownVm < ProxmoxAction
7 |
8 | def initialize app, env
9 | @app = app
10 | @logger = Log4r::Logger.new 'vagrant_proxmox::action::shutdown_vm'
11 | end
12 |
13 | def call env
14 | env[:ui].info I18n.t('vagrant_proxmox.shut_down_vm')
15 | begin
16 | node, vm_id = env[:machine].id.split '/'
17 | exit_status = connection(env).shutdown_vm vm_id
18 | exit_status == 'OK' ? exit_status : raise(VagrantPlugins::Proxmox::Errors::ProxmoxTaskFailed, proxmox_exit_status: exit_status)
19 | rescue StandardError => e
20 | raise VagrantPlugins::Proxmox::Errors::VMShutdownError, proxmox_exit_status: e.message
21 | end
22 | env[:ui].info I18n.t('vagrant_proxmox.done')
23 |
24 | next_action env
25 | end
26 |
27 | end
28 |
29 | end
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/action/start_vm.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 | module Action
4 |
5 | # This action starts the Proxmox virtual machine in env[:machine]
6 | class StartVm < ProxmoxAction
7 |
8 | def initialize app, env
9 | @app = app
10 | @logger = Log4r::Logger.new 'vagrant_proxmox::action::start_vm'
11 | end
12 |
13 | def call env
14 | env[:ui].info I18n.t('vagrant_proxmox.starting_vm')
15 | begin
16 | node, vm_id = env[:machine].id.split '/'
17 | exit_status = connection(env).start_vm vm_id
18 | exit_status == 'OK' ? exit_status : raise(VagrantPlugins::Proxmox::Errors::ProxmoxTaskFailed, proxmox_exit_status: exit_status)
19 | rescue StandardError => e
20 | raise VagrantPlugins::Proxmox::Errors::VMStartError, proxmox_exit_status: e.message
21 | end
22 |
23 | env[:ui].info I18n.t('vagrant_proxmox.done')
24 |
25 | env[:ui].info I18n.t('vagrant_proxmox.waiting_for_ssh_connection')
26 |
27 | retryException = Class.new StandardError
28 |
29 | begin
30 | retryable(on: retryException,
31 | tries: env[:machine].provider_config.ssh_timeout / env[:machine].provider_config.ssh_status_check_interval + 1,
32 | sleep: env[:machine].provider_config.ssh_status_check_interval) do
33 | raise retryException unless env[:interrupted] || env[:machine].communicate.ready?
34 | end
35 | rescue retryException
36 | raise VagrantPlugins::Proxmox::Errors::SSHError
37 | end
38 |
39 | env[:ui].info I18n.t('vagrant_proxmox.done')
40 |
41 | next_action env
42 | end
43 |
44 | end
45 |
46 | end
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/action/stop_vm.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 | module Action
4 |
5 | # This action stops the Proxmox virtual machine in env[:machine]
6 | class StopVm < ProxmoxAction
7 |
8 | def initialize app, env
9 | @app = app
10 | @logger = Log4r::Logger.new 'vagrant_proxmox::action::stop_vm'
11 | end
12 |
13 | def call env
14 | begin
15 | node, vm_id = env[:machine].id.split '/'
16 | env[:ui].info I18n.t('vagrant_proxmox.stopping_vm')
17 | exit_status = connection(env).stop_vm vm_id
18 | exit_status == 'OK' ? exit_status : raise(VagrantPlugins::Proxmox::Errors::ProxmoxTaskFailed, proxmox_exit_status: exit_status)
19 | rescue StandardError => e
20 | raise VagrantPlugins::Proxmox::Errors::VMStopError, proxmox_exit_status: e.message
21 | end
22 |
23 | env[:ui].info I18n.t('vagrant_proxmox.done')
24 |
25 | next_action env
26 | end
27 |
28 | end
29 |
30 | end
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/action/sync_folders.rb:
--------------------------------------------------------------------------------
1 | require 'vagrant/util/subprocess'
2 |
3 | module VagrantPlugins
4 | module Proxmox
5 | module Action
6 |
7 | # This action uses 'rsync' to sync the folders over to the virtual machine.
8 | class SyncFolders < ProxmoxAction
9 |
10 | def initialize app, env
11 | @app = app
12 | @logger = Log4r::Logger.new 'vagrant_proxmox::action::sync_folders'
13 | end
14 |
15 | def call env
16 | ssh_info = env[:machine].ssh_info
17 |
18 | env[:machine].config.vm.synced_folders.each do |_, data|
19 | hostpath = File.expand_path data[:hostpath], env[:root_path]
20 | guestpath = data[:guestpath]
21 | next if data[:disabled]
22 |
23 | # Make sure there is a trailing slash on the host path to
24 | # avoid creating an additional directory with rsync
25 | hostpath = "#{hostpath}/" if hostpath !~ /\/$/
26 |
27 | env[:ui].info I18n.t('vagrant_proxmox.rsync_folder', hostpath: hostpath, guestpath: guestpath)
28 |
29 | # Create the guest path
30 | env[:machine].communicate.sudo "mkdir -p '#{guestpath}'"
31 | env[:machine].communicate.sudo "chown #{ssh_info[:username]} '#{guestpath}'"
32 |
33 | # rsync over to the guest path using the SSH info
34 | command = [
35 | 'rsync', '--verbose', '--archive', '--compress', '--delete',
36 | '-e', "ssh -p #{ssh_info[:port]} -i '#{ssh_info[:private_key_path][0]}' -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null",
37 | hostpath, "#{ssh_info[:username]}@#{ssh_info[:host]}:#{guestpath}"]
38 |
39 | rsync_process = Vagrant::Util::Subprocess.execute *command
40 | if rsync_process.exit_code != 0
41 | raise Errors::RsyncError, guestpath: guestpath, hostpath: hostpath, stderr: rsync_process.stderr
42 | end
43 | end
44 |
45 | next_action env
46 | end
47 |
48 | end
49 |
50 | end
51 | end
52 | end
53 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/action/upload_iso_file.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 | module Action
4 |
5 | # This action uploads a iso file into the local storage a given node
6 | class UploadIsoFile < ProxmoxAction
7 |
8 | def initialize app, env
9 | @app = app
10 | @logger = Log4r::Logger.new 'vagrant_proxmox::action::iso_file_upload'
11 | end
12 |
13 | def call env
14 | env[:result] = :ok
15 | config = env[:machine].provider_config
16 | if config.qemu_iso_file
17 | env[:result] = upload_file env, config.qemu_iso_file, config.replace_qemu_iso_file
18 | end
19 | next_action env
20 | end
21 |
22 | private
23 | def upload_file env, filename, replace
24 | if File.exist? filename
25 | begin
26 | connection(env).upload_file(filename, content_type: 'iso', node: env[:proxmox_selected_node], storage: 'local', replace: replace)
27 | :ok
28 | rescue
29 | :server_upload_error
30 | end
31 | else
32 | :file_not_found
33 | end
34 | end
35 | end
36 | end
37 | end
38 | end
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/action/upload_template_file.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 | module Action
4 |
5 | # This action uploads a template file into the local storage of a given node
6 | class UploadTemplateFile < ProxmoxAction
7 |
8 | def initialize app, env
9 | @app = app
10 | @logger = Log4r::Logger.new 'vagrant_proxmox::action::template_file_upload'
11 | end
12 |
13 | def call env
14 | env[:result] = :ok
15 | config = env[:machine].provider_config
16 | if config.openvz_template_file
17 | env[:result] = upload_file env, config.openvz_template_file, config.replace_openvz_template_file
18 | end
19 | next_action env
20 | end
21 |
22 | private
23 | def upload_file env, filename, replace
24 | if File.exist? filename
25 | begin
26 | connection(env).upload_file(filename, content_type: 'vztmpl', node: env[:proxmox_selected_node], storage: 'local', replace: replace)
27 | :ok
28 | rescue
29 | :server_upload_error
30 | end
31 | else
32 | :file_not_found
33 | end
34 | end
35 | end
36 | end
37 | end
38 | end
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/config.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 | class Config < Vagrant.plugin('2', :config)
4 |
5 | # The Proxmox REST API endpoint
6 | #
7 | # @return [String]
8 | attr_accessor :endpoint
9 |
10 | # The Proxmox preferred cluster node
11 | #
12 | # @return [String]
13 | attr_accessor :selected_node
14 |
15 | # The Proxmox user name
16 | #
17 | # @return [String]
18 | attr_accessor :user_name
19 |
20 | # The Proxmox password
21 | #
22 | # @return [String]
23 | attr_accessor :password
24 |
25 | # The virtual machine type, e.g. :openvz or :qemu or :lxc
26 | #
27 | # @return [Symbol]
28 | attr_accessor :vm_type
29 |
30 | # The openvz os template to use for the virtual machine
31 | #
32 | # @return [String]
33 | attr_accessor :openvz_os_template
34 |
35 | # The openvz os template file to upload and use for the virtual machine
36 | #
37 | # @return [String]
38 | attr_accessor :openvz_template_file
39 |
40 | # Should the openvz os template be replaced (deleted before upload)?
41 | #
42 | # @return [Boolean]
43 | attr_accessor :replace_openvz_template_file
44 |
45 | # The id range to use for the virtual machines
46 | #
47 | # @return [Range]
48 | attr_accessor :vm_id_range
49 |
50 | # The prefix for the virtual machine name
51 | #
52 | # @return [String]
53 | attr_accessor :vm_name_prefix
54 |
55 | # Amount of RAM for the virtual machine in MB
56 | #
57 | # @return [Integer]
58 | attr_accessor :vm_memory
59 |
60 | # The vm disk size to use for the virtual machine, e.g. '30G'
61 | #
62 | # @return [String]
63 | attr_accessor :vm_disk_size
64 |
65 | # The vm storage to use for the virtual machine, e.g. 'local', 'raid', 'cephstore'
66 | # defaults to 'raid' for backwards compatability
67 | #
68 | # @return [String]
69 | attr_accessor :vm_storage
70 |
71 | # The maximum timeout for a proxmox server task (in seconds)
72 | #
73 | # @return [Integer]
74 | attr_accessor :task_timeout
75 |
76 | # The interval between two proxmox task status retrievals (in seconds)
77 | #
78 | # @return [Integer, Proc]
79 | attr_accessor :task_status_check_interval
80 |
81 | # The maximum timeout for a ssh connection to a virtual machine (in seconds)
82 | #
83 | # @return [Integer]
84 | attr_accessor :ssh_timeout
85 |
86 | # The interval between two ssh reachability status retrievals (in seconds)
87 | #
88 | # @return [Integer, Proc]
89 | attr_accessor :ssh_status_check_interval
90 |
91 | # The maximum timeout for a proxmox server task if it's an upload (in seconds)
92 | #
93 | # @return [Integer]
94 | attr_accessor :imgcopy_timeout
95 |
96 | # The qemu virtual machine operating system, e.g. :l26
97 | #
98 | # @return [Symbol]
99 | attr_accessor :qemu_os
100 |
101 | # The number of cores per socket
102 | #
103 | # @return [Integer]
104 | attr_accessor :qemu_cores
105 |
106 | # The number of CPU sockets
107 | #
108 | # @return [Integer]
109 | attr_accessor :qemu_sockets
110 |
111 | # The qemu template to clone for the virtual machine
112 | #
113 | # @return [String]
114 | attr_accessor :qemu_template
115 |
116 | # The qemu iso file to use for the virtual machine
117 | #
118 | # @return [String]
119 | attr_accessor :qemu_iso
120 |
121 | # The qemu iso file to upload and use for the virtual machine
122 | #
123 | # @return [String]
124 | attr_accessor :qemu_iso_file
125 |
126 | # Should the qemu iso file replaced (deleted before upload)?
127 | #
128 | # @return [Boolean]
129 | attr_accessor :replace_qemu_iso_file
130 |
131 | # The qemu disk size to use for the virtual machine, e.g. '30G'
132 | #
133 | # @return [String]
134 | attr_accessor :qemu_disk_size
135 |
136 | # The qemu storage to use for the virtual machine, e.g. 'local', 'raid', 'cephstore'
137 | # defaults to 'raid' for backwards compatability
138 | #
139 | # @return [String]
140 | attr_accessor :qemu_storage
141 |
142 | # The qemu network interface card model, e.g. 'e1000', 'virtio'
143 | # defaults to 'e1000' for backwards compatability
144 | #
145 | # @return [String]
146 | attr_accessor :qemu_nic_model
147 |
148 | # The qemu network bridge, e.g. 'vmbr0'
149 | # defaults to 'vmbr0' for backwards compatability
150 | #
151 | # @return [String]
152 | attr_accessor :qemu_bridge
153 |
154 | def initialize
155 | @endpoint = UNSET_VALUE
156 | @selected_node = UNSET_VALUE
157 | @user_name = UNSET_VALUE
158 | @password = UNSET_VALUE
159 | @vm_type = UNSET_VALUE
160 | @openvz_os_template = UNSET_VALUE
161 | @openvz_template_file = UNSET_VALUE
162 | @replace_openvz_template_file = false
163 | @vm_id_range = 900..999
164 | @vm_name_prefix = 'vagrant_'
165 | @vm_memory = 512
166 | @vm_disk_size = '20G'
167 | @vm_storage = 'local'
168 | @task_timeout = 60
169 | @task_status_check_interval = 2
170 | @ssh_timeout = 60
171 | @ssh_status_check_interval = 5
172 | @imgcopy_timeout = 120
173 | @qemu_os = UNSET_VALUE
174 | @qemu_cores = 1
175 | @qemu_sockets = 1
176 | @qemu_template = UNSET_VALUE
177 | @qemu_iso = UNSET_VALUE
178 | @qemu_iso_file = UNSET_VALUE
179 | @replace_qemu_iso_file = false
180 | @qemu_disk_size = UNSET_VALUE
181 | @qemu_storage = 'raid'
182 | @qemu_nic_model = 'e1000'
183 | @qemu_bridge = 'vmbr0'
184 | end
185 |
186 | # This is the hook that is called to finalize the object before it is put into use.
187 | def finalize!
188 | @endpoint = nil if @endpoint == UNSET_VALUE
189 | @selected_node = nil if @endpoint == UNSET_VALUE
190 | @user_name = nil if @user_name == UNSET_VALUE
191 | @password = nil if @password == UNSET_VALUE
192 | @vm_type = nil if @vm_type == UNSET_VALUE
193 | @openvz_template_file = nil if @openvz_template_file == UNSET_VALUE
194 | @openvz_os_template = "local:vztmpl/#{File.basename @openvz_template_file}" if @openvz_template_file
195 | @openvz_os_template = nil if @openvz_os_template == UNSET_VALUE
196 | @qemu_template = nil if @qemu_os == UNSET_VALUE
197 | @qemu_os = nil if @qemu_os == UNSET_VALUE
198 | @qemu_iso_file = nil if @qemu_iso_file == UNSET_VALUE
199 | @qemu_iso = "local:iso/#{File.basename @qemu_iso_file}" if @qemu_iso_file
200 | @qemu_iso = nil if @qemu_iso == UNSET_VALUE
201 | @qemu_disk_size = nil if @qemu_disk_size == UNSET_VALUE
202 | @qemu_disk_size = convert_disk_size_to_gigabyte @qemu_disk_size if @qemu_disk_size
203 | @vm_disk_size = convert_disk_size_to_gigabyte @vm_disk_size if @vm_disk_size
204 | end
205 |
206 | def validate machine
207 | errors = []
208 | errors << I18n.t('vagrant_proxmox.errors.no_endpoint_specified') unless @endpoint
209 | errors << I18n.t('vagrant_proxmox.errors.no_user_name_specified') unless @user_name
210 | errors << I18n.t('vagrant_proxmox.errors.no_password_specified') unless @password
211 | errors << I18n.t('vagrant_proxmox.errors.no_vm_type_specified') unless @vm_type
212 | if @vm_type == :openvz
213 | errors << I18n.t('vagrant_proxmox.errors.no_openvz_os_template_or_openvz_template_file_specified_for_type_openvz') unless @openvz_os_template || @openvz_template_file
214 | end
215 | if @vm_type == :qemu
216 | if @qemu_template
217 | else
218 | errors << I18n.t('vagrant_proxmox.errors.no_qemu_os_specified_for_vm_type_qemu') unless @qemu_os
219 | errors << I18n.t('vagrant_proxmox.errors.no_qemu_iso_or_qemu_iso_file_specified_for_vm_type_qemu') unless @qemu_iso || @qemu_iso_file
220 | errors << I18n.t('vagrant_proxmox.errors.no_qemu_disk_size_specified_for_vm_type_qemu') unless @qemu_disk_size
221 | end
222 | end
223 | {'Proxmox Provider' => errors}
224 | end
225 |
226 | private
227 | def convert_disk_size_to_gigabyte disk_size
228 | case disk_size[-1]
229 | when 'G'
230 | disk_size[0..-2]
231 | else
232 | disk_size
233 | end
234 | end
235 | end
236 | end
237 | end
238 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/errors.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 |
4 | class ProxmoxTaskNotFinished < Exception
5 | end
6 |
7 | module Errors
8 |
9 | class VagrantProxmoxError < Vagrant::Errors::VagrantError
10 | error_namespace 'vagrant_proxmox.errors'
11 | end
12 |
13 | class ProxmoxTaskFailed < VagrantProxmoxError
14 | error_key :proxmox_task_failed
15 | end
16 |
17 | class CommunicationError < VagrantProxmoxError
18 | error_key :communication_error
19 | end
20 |
21 | class Timeout < VagrantProxmoxError
22 | error_key :timeout
23 | end
24 |
25 | class NoVmIdAvailable < VagrantProxmoxError
26 | error_key :no_vm_id_available
27 | end
28 |
29 | class VMCreateError < VagrantProxmoxError
30 | error_key :vm_create_error
31 | end
32 |
33 | class VMCloneError < VagrantProxmoxError
34 | error_key :vm_clone_error
35 | end
36 |
37 | class NoTemplateAvailable < VagrantProxmoxError
38 | error_key :no_template_available
39 | end
40 |
41 | class VMConfigError < VagrantProxmoxError
42 | error_key :vm_configure_error
43 | end
44 |
45 | class VMDestroyError < VagrantProxmoxError
46 | error_key :vm_destroy_error
47 | end
48 |
49 | class VMStartError < VagrantProxmoxError
50 | error_key :vm_start_error
51 | end
52 |
53 | class VMStopError < VagrantProxmoxError
54 | error_key :vm_stop_error
55 | end
56 |
57 | class VMShutdownError < VagrantProxmoxError
58 | error_key :vm_shutdown_error
59 | end
60 |
61 | class RsyncError < VagrantProxmoxError
62 | error_key :rsync_error
63 | end
64 |
65 | class SSHError < VagrantProxmoxError
66 | error_key :ssh_error
67 | end
68 |
69 | class InvalidNodeError < VagrantProxmoxError
70 | error_key :invalid_node_error
71 | end
72 |
73 | end
74 | end
75 | end
76 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/plugin.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 | class Plugin < Vagrant.plugin ('2')
4 |
5 | name 'Proxmox'
6 | description <<-DESC
7 | This plugin installs a provider that allows Vagrant to manage
8 | machines on Proxmox.
9 | DESC
10 |
11 | config(:proxmox, :provider) do
12 | require_relative 'config'
13 | Config
14 | end
15 |
16 | provider(:proxmox, parallel: true) do
17 | # Setup logging and i18n
18 | setup_logging
19 | setup_i18n
20 |
21 | # Return the provider
22 | require_relative 'provider'
23 | Provider
24 | end
25 |
26 | # This initializes the internationalization strings.
27 | def self.setup_i18n
28 | I18n.load_path << File.expand_path('locales/en.yml', Proxmox.source_root)
29 | I18n.reload!
30 | end
31 |
32 | # This sets up our log level to be whatever VAGRANT_LOG is.
33 | def self.setup_logging
34 | require 'log4r'
35 |
36 | level = nil
37 | begin
38 | level = Log4r.const_get ENV['VAGRANT_LOG'].upcase
39 | rescue NameError
40 | # This means that the logging constant wasn't found,
41 | # which is fine. We just keep `level` as `nil`. But
42 | # we tell the user.
43 | level = nil
44 | end
45 |
46 | # Some constants, such as "true" resolve to booleans, so the
47 | # above error checking doesn't catch it. This will check to make
48 | # sure that the log level is an integer, as Log4r requires.
49 | level = nil if !level.is_a?(Integer)
50 |
51 | # Set the logging level on all "vagrant" namespaced
52 | # logs as long as we have a valid level.
53 | if level
54 | logger = Log4r::Logger.new 'vagrant_proxmox'
55 | logger.outputters = Log4r::Outputter.stderr
56 | logger.level = level
57 | end
58 | end
59 | end
60 | end
61 | end
62 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/provider.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 |
4 | class Provider < Vagrant.plugin('2', :provider)
5 |
6 | def initialize machine
7 | @machine = machine
8 | end
9 |
10 | def action name
11 | # Attempt to get the action method from the Action class if it
12 | # exists, otherwise return nil to show that we don't support the
13 | # given action.
14 | action_method = "action_#{name}"
15 | return Action.send(action_method) if Action.respond_to?(action_method)
16 | nil
17 | end
18 |
19 | def state
20 | # Run a custom action we define called "read_state" which does
21 | # what it says. It puts the state in the `:machine_state_id`
22 | # key in the environment.
23 | env = @machine.action 'read_state'
24 |
25 | state_id = env[:machine_state_id]
26 |
27 | # Get the short and long description
28 | short = I18n.t "vagrant_proxmox.states.short_#{state_id}"
29 | long = I18n.t "vagrant_proxmox.states.long_#{state_id}"
30 |
31 | # Return the MachineState object
32 | Vagrant::MachineState.new state_id, short, long
33 | end
34 |
35 | def ssh_info
36 | # Run a custom action called "read_ssh_info" which does what it
37 | # says and puts the resulting SSH info into the `:machine_ssh_info`
38 | # key in the environment.
39 | env = @machine.action 'read_ssh_info'
40 | env[:machine_ssh_info]
41 | end
42 |
43 | def to_s
44 | id = @machine.id.nil? ? 'new' : @machine.id
45 | "Proxmox (#{id})"
46 | end
47 |
48 | end
49 |
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/proxmox/connection.rb:
--------------------------------------------------------------------------------
1 | require 'vagrant-proxmox/proxmox/errors'
2 | require 'rest-client'
3 | require 'retryable'
4 | require 'required_parameters'
5 |
6 | # Fix wrong header unescaping in RestClient library.
7 | module RestClient
8 | class Request
9 | def make_headers user_headers
10 | unless @cookies.empty?
11 | user_headers[:cookie] = @cookies.map { |(key, val)| "#{key.to_s}=#{val}" }.sort.join('; ')
12 | end
13 | headers = stringify_headers(default_headers).merge(stringify_headers(user_headers))
14 | headers.merge!(@payload.headers) if @payload
15 | headers
16 | end
17 | end
18 | end
19 |
20 | module VagrantPlugins
21 | module Proxmox
22 | class Connection
23 |
24 | include RequiredParameters
25 |
26 | attr_reader :api_url
27 | attr_reader :ticket
28 | attr_reader :csrf_token
29 | attr_accessor :vm_id_range
30 | attr_accessor :task_timeout
31 | attr_accessor :task_status_check_interval
32 | attr_accessor :imgcopy_timeout
33 |
34 | def initialize api_url, opts = {}
35 | @api_url = api_url
36 | @vm_id_range = opts[:vm_id_range] || (900..999)
37 | @task_timeout = opts[:task_timeout] || 60
38 | @task_status_check_interval = opts[:task_status_check_interval] || 2
39 | @imgcopy_timeout = opts[:imgcopy_timeout] || 120
40 | end
41 |
42 | def login username: required('username'), password: required('password')
43 | begin
44 | response = post "/access/ticket", username: username, password: password
45 | @ticket = response[:data][:ticket]
46 | @csrf_token = response[:data][:CSRFPreventionToken]
47 | rescue ApiError::ServerError
48 | raise ApiError::InvalidCredentials
49 | rescue => x
50 | raise ApiError::ConnectionError, x.message
51 | end
52 | end
53 |
54 | def get_node_list
55 | nodelist = get '/nodes'
56 | nodelist[:data].map { |n| n[:node] }
57 | end
58 |
59 | def get_vm_state vm_id
60 | vm_info = get_vm_info vm_id
61 | if vm_info
62 | begin
63 | response = get "/nodes/#{vm_info[:node]}/#{vm_info[:type]}/#{vm_id}/status/current"
64 | states = {'running' => :running,
65 | 'stopped' => :stopped}
66 | states[response[:data][:status]]
67 | rescue ApiError::ServerError
68 | :not_created
69 | end
70 | else
71 | :not_created
72 | end
73 | end
74 |
75 | def wait_for_completion task_response: required('task_response'), timeout_message: required('timeout_message')
76 | task_upid = task_response[:data]
77 | timeout = task_timeout
78 | task_type = /UPID:.*?:.*?:.*?:.*?:(.*)?:.*?:.*?:/.match(task_upid)[1]
79 | timeout = imgcopy_timeout if task_type == 'imgcopy'
80 | begin
81 | retryable(on: VagrantPlugins::Proxmox::ProxmoxTaskNotFinished,
82 | tries: timeout / task_status_check_interval + 1,
83 | sleep: task_status_check_interval) do
84 | exit_status = get_task_exitstatus task_upid
85 | exit_status.nil? ? raise(VagrantPlugins::Proxmox::ProxmoxTaskNotFinished) : exit_status
86 | end
87 | rescue VagrantPlugins::Proxmox::ProxmoxTaskNotFinished
88 | raise VagrantPlugins::Proxmox::Errors::Timeout.new timeout_message
89 | end
90 | end
91 |
92 | def delete_vm vm_id
93 | vm_info = get_vm_info vm_id
94 | response = delete "/nodes/#{vm_info[:node]}/#{vm_info[:type]}/#{vm_id}"
95 | wait_for_completion task_response: response, timeout_message: 'vagrant_proxmox.errors.destroy_vm_timeout'
96 | end
97 |
98 | def create_vm node: required('node'), vm_type: required('node'), params: required('params')
99 | response = post "/nodes/#{node}/#{vm_type}", params
100 | wait_for_completion task_response: response, timeout_message: 'vagrant_proxmox.errors.create_vm_timeout'
101 | end
102 |
103 | def clone_vm node: required('node'), vm_type: required('node'), params: required('params')
104 | vm_id = params[:vmid]
105 | params.delete(:vmid)
106 | params.delete(:ostype)
107 | params.delete(:ide2)
108 | params.delete(:sata0)
109 | params.delete(:sockets)
110 | params.delete(:cores)
111 | params.delete(:description)
112 | params.delete(:memory)
113 | params.delete(:net0)
114 | response = post "/nodes/#{node}/#{vm_type}/#{vm_id}/clone", params
115 | wait_for_completion task_response: response, timeout_message: 'vagrant_proxmox.errors.create_vm_timeout'
116 | end
117 |
118 | def config_clone node: required('node'), vm_type: required('node'), params: required('params')
119 | vm_id = params[:vmid]
120 | params.delete(:vmid)
121 | response = post "/nodes/#{node}/#{vm_type}/#{vm_id}/config", params
122 | wait_for_completion task_response: response, timeout_message: 'vagrant_proxmox.errors.create_vm_timeout'
123 | end
124 |
125 | def get_vm_config node: required('node'), vm_id: required('node'), vm_type: required('node')
126 | response = get "/nodes/#{node}/#{vm_type}/#{vm_id}/config"
127 | response = response[:data]
128 | response.empty? ? raise(VagrantPlugins::Proxmox::Errors::VMConfigError) : response
129 | end
130 |
131 | def start_vm vm_id
132 | vm_info = get_vm_info vm_id
133 | response = post "/nodes/#{vm_info[:node]}/#{vm_info[:type]}/#{vm_id}/status/start", nil
134 | wait_for_completion task_response: response, timeout_message: 'vagrant_proxmox.errors.start_vm_timeout'
135 | end
136 |
137 | def stop_vm vm_id
138 | vm_info = get_vm_info vm_id
139 | response = post "/nodes/#{vm_info[:node]}/#{vm_info[:type]}/#{vm_id}/status/stop", nil
140 | wait_for_completion task_response: response, timeout_message: 'vagrant_proxmox.errors.stop_vm_timeout'
141 | end
142 |
143 | def shutdown_vm vm_id
144 | vm_info = get_vm_info vm_id
145 | response = post "/nodes/#{vm_info[:node]}/#{vm_info[:type]}/#{vm_id}/status/shutdown", nil
146 | wait_for_completion task_response: response, timeout_message: 'vagrant_proxmox.errors.shutdown_vm_timeout'
147 | end
148 |
149 | def get_free_vm_id
150 | # to avoid collisions in multi-vm setups
151 | sleep (rand(1..3) + 0.1 * rand(0..9))
152 | response = get "/cluster/resources?type=vm"
153 | allowed_vm_ids = vm_id_range.to_set
154 | used_vm_ids = response[:data].map { |vm| vm[:vmid] }
155 | free_vm_ids = (allowed_vm_ids - used_vm_ids).sort
156 | free_vm_ids.empty? ? raise(VagrantPlugins::Proxmox::Errors::NoVmIdAvailable) : free_vm_ids.first
157 | end
158 |
159 | def get_qemu_template_id template
160 | response = get "/cluster/resources?type=vm"
161 | found_ids = response[:data].select { |vm| vm[:type] == 'qemu' }.select { |vm| vm[:template] == 1 }.select { |vm| vm[:name] == template }.map { |vm| vm[:vmid] }
162 | found_ids.empty? ? raise(VagrantPlugins::Proxmox::Errors::NoTemplateAvailable) : found_ids.first
163 | end
164 |
165 | def upload_file file, content_type: required('content_type'), node: required('node'), storage: required('storage'), replace: false
166 | delete_file(filename: file, content_type: content_type, node: node, storage: storage) if replace
167 | unless is_file_in_storage? filename: file, node: node, storage: storage
168 | res = post "/nodes/#{node}/storage/#{storage}/upload", content: content_type,
169 | filename: File.new(file, 'rb'), node: node, storage: storage
170 | wait_for_completion task_response: res, timeout_message: 'vagrant_proxmox.errors.upload_timeout'
171 | end
172 | end
173 |
174 | def delete_file filename: required('filename'), content_type: required('content_type'), node: required('node'), storage: required('storage')
175 | delete "/nodes/#{node}/storage/#{storage}/content/#{content_type}/#{File.basename filename}"
176 | end
177 |
178 | def list_storage_files node: required('node'), storage: required('storage')
179 | res = get "/nodes/#{node}/storage/#{storage}/content"
180 | res[:data].map { |e| e[:volid] }
181 | end
182 |
183 | def get_node_ip node, interface
184 | begin
185 | response = get "/nodes/#{node}/network/#{interface}"
186 | response[:data][:address]
187 | rescue ApiError::ServerError
188 | :not_created
189 | end
190 | end
191 |
192 | # This is called every time to retrieve the node and vm_type, hence on large
193 | # installations this could be a huge amount of data. Probably an optimization
194 | # with a buffer for the machine info could be considered.
195 | private
196 | def get_vm_info vm_id
197 | response = get '/cluster/resources?type=vm'
198 | response[:data]
199 | .select { |m| m[:id] =~ /^[a-z]*\/#{vm_id}$/ }
200 | .map { |m| {id: vm_id, type: /^(.*)\/(.*)$/.match(m[:id])[1], node: m[:node]} }
201 | .first
202 | end
203 |
204 | private
205 | def get_task_exitstatus task_upid
206 | node = /UPID:(.*?):/.match(task_upid)[1]
207 | response = get "/nodes/#{node}/tasks/#{task_upid}/status"
208 | response[:data][:exitstatus]
209 | end
210 |
211 | private
212 | def get path
213 | begin
214 | response = RestClient.get "#{api_url}#{path}", {cookies: {PVEAuthCookie: ticket}}
215 | JSON.parse response.to_s, symbolize_names: true
216 | rescue RestClient::NotImplemented
217 | raise ApiError::NotImplemented
218 | rescue RestClient::InternalServerError
219 | raise ApiError::ServerError
220 | rescue RestClient::Unauthorized
221 | raise ApiError::UnauthorizedError
222 | rescue => x
223 | raise ApiError::ConnectionError, x.message
224 | end
225 | end
226 |
227 | private
228 | def delete path, params = {}
229 | begin
230 | response = RestClient.delete "#{api_url}#{path}", headers
231 | JSON.parse response.to_s, symbolize_names: true
232 | rescue RestClient::Unauthorized
233 | raise ApiError::UnauthorizedError
234 | rescue RestClient::NotImplemented
235 | raise ApiError::NotImplemented
236 | rescue RestClient::InternalServerError
237 | raise ApiError::ServerError
238 | rescue => x
239 | raise ApiError::ConnectionError, x.message
240 | end
241 | end
242 |
243 | private
244 | def post path, params = {}
245 | begin
246 | response = RestClient.post "#{api_url}#{path}", params, headers
247 | JSON.parse response.to_s, symbolize_names: true
248 | rescue RestClient::Unauthorized
249 | raise ApiError::UnauthorizedError
250 | rescue RestClient::NotImplemented
251 | raise ApiError::NotImplemented
252 | rescue RestClient::InternalServerError
253 | raise ApiError::ServerError
254 | rescue => x
255 | raise ApiError::ConnectionError, x.message
256 | end
257 | end
258 |
259 | private
260 | def headers
261 | ticket.nil? ? {} : {CSRFPreventionToken: csrf_token, cookies: {PVEAuthCookie: ticket}}
262 | end
263 |
264 | private
265 | def is_file_in_storage? filename: required('filename'), node: required('node'), storage: required('storage')
266 | (list_storage_files node: node, storage: storage).find { |f| f =~ /#{File.basename filename}/ }
267 | end
268 | end
269 | end
270 | end
271 |
272 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/proxmox/errors.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 |
4 | module ApiError
5 |
6 | class InvalidCredentials < StandardError
7 | end
8 |
9 | class ConnectionError < StandardError
10 | end
11 |
12 | class NotImplemented < StandardError
13 | end
14 |
15 | class ServerError < StandardError
16 | end
17 |
18 | class UnauthorizedError < StandardError
19 | end
20 |
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/lib/vagrant-proxmox/version.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins
2 | module Proxmox
3 |
4 | VERSION = '0.0.10'
5 |
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/local_test.rb:
--------------------------------------------------------------------------------
1 | require 'yaml'
2 | $:<<"./lib/"
3 | require 'rest-client'
4 | # require 'vagrant-proxmox/proxmox/connection'
5 | require 'json'
6 | require 'vagrant-proxmox'
7 | require_relative 'features/support/vagrant_ui_mock.rb'
8 |
9 |
10 | config=YAML.load_file("#{ENV['HOME']}/.rake/rake.yml")
11 | @conn=VagrantPlugins::Proxmox::Connection.new config['proxmox']['endpoint']
12 | @conn.login username: config['proxmox']['user_name'] , password: config['proxmox']['password']
13 |
14 | @environment = Vagrant::Environment.new vagrantfile_name: 'Vagrantfile_qemu'
15 | @ui = VagrantUIMock.new
16 | @environment.instance_variable_set :@ui, @ui
17 |
18 | # Vagrant.plugin('2').manager.commands[:up].new(['--provider=proxmox'], @environment).execute
--------------------------------------------------------------------------------
/locales/en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | vagrant_proxmox:
3 | already_running: 'The virtual machine is already up and running'
4 | already_stopped: "The virtual machine is already stopped"
5 | creating_vm: 'Creating the virtual machine...'
6 | destroying_vm: 'Destroying the virtual machine...'
7 | done: "Done!"
8 | errors:
9 | create_vm_timeout: |-
10 | Creating the vm on the proxmox server timed out.
11 | communication_error: |-
12 | Unable to communicate with proxmox server:
13 |
14 | %{error_msg}
15 | destroy_vm_timeout: |-
16 | Destroying the vm on the proxmox server timed out.
17 | no_endpoint_specified: 'No endpoint specified.'
18 | no_openvz_os_template_or_openvz_template_file_specified_for_type_openvz: 'No openvz_os_template or openvz_template_file specified for vm_type=:openvz'
19 | no_password_specified: 'No password specified.'
20 | no_user_name_specified: 'No user_name specified.'
21 | no_vm_id_available: |-
22 | Unable to create a new virtual machine on the proxmox server.
23 | None of the configured vm_ids is available.
24 | proxmox_task_failed: |-
25 | A task on the proxmox server failed.
26 | It returned the following exit status:
27 |
28 | %{proxmox_exit_status}
29 | rsync_error: |-
30 | There was an error when attemping to rsync a share folder.
31 | Please inspect the error message below for more info.
32 |
33 | Host path: %{hostpath}
34 | Guest path: %{guestpath}
35 | Error: %{stderr}
36 | shutdown_vm_timeout: |-
37 | Stopping the vm on the proxmox server timed out.
38 | start_vm_timeout: |-
39 | Starting the vm on the proxmox server timed out.
40 | stop_vm_timeout: |-
41 | Stopping the vm on the proxmox server timed out.
42 | timeout: 'Timeout error'
43 | upload_timeout: |-
44 | Uploading a file to the proxmox server timed out.
45 | vm_create_error: |-
46 | Unable to create the virtual machine!
47 |
48 | Cause: %{proxmox_exit_status}
49 | vm_destroy_error: |-
50 | Unable to destroy the virtual machine!
51 |
52 | Cause: %{proxmox_exit_status}
53 | vm_start_error: |-
54 | Unable to start the virtual machine!
55 |
56 | Cause: %{proxmox_exit_status}
57 | vm_stop_error: |-
58 | Unable to stop the virtual machine!
59 |
60 | Cause: %{proxmox_exit_status}
61 | vm_shutdown_error: |-
62 | Unable to shutdown the virtual machine!
63 |
64 | Cause: %{proxmox_exit_status}
65 | vm_not_running: |-
66 | VM must be running to execute this command. Run `vagrant up`
67 | to start the virtual machine.
68 | file_not_found: "File for upload not found"
69 | server_upload_error: "Error during upload"
70 | ssh_error: Unable to establish an ssh connection to the virtual machine...
71 | no_vm_type_specified: "No vm_type specified"
72 | no_qemu_iso_or_qemu_iso_file_specified_for_vm_type_openvz: "No qemu_iso or qemu_iso_file specified for vm_type=:qemu"
73 | no_qemu_iso_or_qemu_iso_file_specified_for_vm_type_qemu: "No qemu_iso or qemu_iso_file specified for vm_type=:qemu"
74 | no_qemu_os_specified_for_vm_type_qemu: "No qemu_os specified for vm_type=:qemu"
75 | no_qemu_disk_size_specified_for_vm_type_qemu: "No qemu_disk_size specified for vm_type=:qemu"
76 | invalid_node_error: 'Invalid node specified: %{node}'
77 | not_created: 'The virtual machine is not created on the server!'
78 | rsync_folder: |-
79 | Rsyncing folder: %{hostpath} => %{guestpath}
80 | shut_down_vm: 'Shutting down the virtual machine...'
81 | starting_vm: 'Starting the virtual machine...'
82 | states:
83 | long_not_created: |-
84 | The server is not created. Run `vagrant up` to create it.
85 | long_running: |-
86 | The server is up and running. Run `vagrant ssh` to access it.
87 | long_stopped: |-
88 | The server is stopped.
89 | short_not_created: |-
90 | not created
91 | short_running: |-
92 | running
93 | short_stopped: |-
94 | stopped
95 | stopping_vm: 'Stopping the virtual machine...'
96 | waiting_for_ssh_connection: 'Waiting for SSH connection...'
97 |
--------------------------------------------------------------------------------
/spec/actions/cleanup_after_destroy_action_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'actions/proxmox_action_shared'
3 |
4 | describe VagrantPlugins::Proxmox::Action::CleanupAfterDestroy do
5 |
6 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
7 | let(:env) { {machine: environment.machine(environment.primary_machine_name, :proxmox)} }
8 | let(:ui) { double('ui').as_null_object }
9 |
10 | subject(:action) { described_class.new(-> (_) {}, environment) }
11 |
12 | it_behaves_like 'a proxmox action call'
13 |
14 | describe '#call', :need_box do
15 | it 'should delete the directory `.vagrant/[:machine].name`' do
16 | expect do
17 | action.call env
18 | end.to change{File.exists?(".vagrant/machines/#{env[:machine].name}/proxmox")}.to false
19 | end
20 | end
21 |
22 | end
23 |
--------------------------------------------------------------------------------
/spec/actions/connect_proxmox_action_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'actions/proxmox_action_shared'
3 |
4 | module VagrantPlugins::Proxmox
5 |
6 | describe Action::ConnectProxmox do
7 |
8 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
9 | let(:env) { {machine: environment.machine(environment.primary_machine_name, :proxmox)} }
10 | let(:api_url) { 'http://your.proxmox.machine/api' }
11 | let(:username) { 'user' }
12 | let(:password) { 'password' }
13 | let(:connection) { env[:proxmox_connection] }
14 |
15 | subject(:action) { described_class.new(-> (_) {}, environment) }
16 |
17 | before { VagrantPlugins::Proxmox::Plugin.setup_i18n }
18 |
19 | describe '#call' do
20 |
21 | before do
22 | env[:machine].provider_config.endpoint = api_url
23 | env[:machine].provider_config.user_name = username
24 | env[:machine].provider_config.password = password
25 | env[:machine].provider_config.vm_id_range = 500..555
26 | env[:machine].provider_config.task_timeout = 123
27 | env[:machine].provider_config.task_status_check_interval = 5
28 | env[:machine].provider_config.imgcopy_timeout = 99
29 | allow_any_instance_of(Connection).to receive :login
30 | end
31 |
32 | it_behaves_like 'a proxmox action call'
33 |
34 | it 'should store a connection object in env[:proxmox_connection]' do
35 | action.call env
36 | expect(connection.api_url).to eq(api_url)
37 | end
38 |
39 | describe 'sets the connection configuration parameters' do
40 | before { action.call env }
41 | specify { expect(connection.vm_id_range).to eq(500..555) }
42 | specify { expect(connection.task_timeout).to eq(123) }
43 | specify { expect(connection.task_status_check_interval).to eq(5) }
44 | specify { expect(connection.imgcopy_timeout).to eq(99) }
45 | end
46 |
47 | it 'should call the login function with credentials from configuration' do
48 | expect_any_instance_of(Connection).to receive(:login).with username: username, password: password
49 | action.call env
50 | end
51 |
52 | context 'when the server communication fails' do
53 |
54 | before { allow_any_instance_of(Connection).to receive(:login).and_raise ApiError::InvalidCredentials }
55 |
56 | it 'should raise an error' do
57 | expect { action.call env }.to raise_error Errors::CommunicationError
58 | end
59 |
60 | end
61 |
62 | end
63 |
64 | end
65 |
66 | end
67 |
--------------------------------------------------------------------------------
/spec/actions/create_vm_action_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'actions/proxmox_action_shared'
3 |
4 | module VagrantPlugins::Proxmox
5 |
6 | describe Action::CreateVm do
7 |
8 | let(:vagrantfile) { 'dummy_box/Vagrantfile' }
9 | let(:environment) { Vagrant::Environment.new vagrantfile_name: vagrantfile }
10 | let(:connection) { Connection.new 'https://your.proxmox.server/api2/json' }
11 | let(:env) { {machine: environment.machine(environment.primary_machine_name, :proxmox),
12 | proxmox_selected_node: 'localhost',
13 | ui: double('ui').as_null_object,
14 | proxmox_connection: connection} }
15 | let(:app) { double('app').as_null_object }
16 | let(:task_upid) { 'UPID:localhost:0000F6ED:00F8E25F:5268CD3B:vzcreate:100:vagrant@pve:' }
17 |
18 | subject(:action) { described_class.new(-> (_) {}, environment) }
19 |
20 | before do
21 | allow(connection).to receive_messages :get_free_vm_id => 100
22 | allow(connection).to receive_messages :create_vm => 'OK'
23 | end
24 |
25 | describe '#call' do
26 |
27 | it_behaves_like 'a proxmox action call'
28 |
29 | describe 'the call to create a new virtual machine' do
30 |
31 | context 'when the vm_type is :openvz' do
32 |
33 | let(:vagrantfile) { 'dummy_box/Vagrantfile' }
34 | before { allow(env[:machine].provider_config).to receive(:vm_type) { :openvz } }
35 |
36 | context 'with default config' do
37 | specify do
38 | expect(connection).to receive(:create_vm).
39 | with(node: 'localhost',
40 | vm_type: :openvz,
41 | params: {vmid: 100,
42 | hostname: 'machine',
43 | ostemplate: 'local:vztmpl/template.tar.gz',
44 | password: 'vagrant',
45 | memory: 256,
46 | description: 'vagrant_test_machine'})
47 | action.call env
48 | end
49 | end
50 |
51 | context 'with a specified hostname' do
52 | before { env[:machine].config.vm.hostname = 'hostname' }
53 | specify do
54 | expect(connection).to receive(:create_vm).
55 | with(node: 'localhost',
56 | vm_type: :openvz,
57 | params: {vmid: 100,
58 | hostname: 'hostname',
59 | ostemplate: 'local:vztmpl/template.tar.gz',
60 | password: 'vagrant',
61 | memory: 256,
62 | description: 'vagrant_test_machine'})
63 | action.call env
64 | end
65 | end
66 |
67 | context 'with a specified ip address' do
68 | before { allow(env[:machine].config.vm).to receive(:networks) { [[:public_network, {ip: '127.0.0.1'}]] } }
69 | specify do
70 | expect(connection).to receive(:create_vm).
71 | with(node: 'localhost',
72 | vm_type: :openvz,
73 | params: {vmid: 100,
74 | hostname: 'machine',
75 | ip_address: '127.0.0.1',
76 | ostemplate: 'local:vztmpl/template.tar.gz',
77 | password: 'vagrant',
78 | memory: 256,
79 | description: 'vagrant_test_machine'})
80 | action.call env
81 | end
82 | end
83 | end
84 |
85 | context 'when the vm_type is :qemu' do
86 |
87 | let(:vagrantfile) { 'dummy_box/Vagrantfile_qemu' }
88 | before { allow(env[:machine].provider_config).to receive(:vm_type) { :qemu } }
89 |
90 | context 'with default config' do
91 | specify do
92 | expect(connection).to receive(:create_vm).
93 | with(node: 'localhost',
94 | vm_type: :qemu,
95 | params: {vmid: 100,
96 | name: 'machine',
97 | ostype: :l26,
98 | ide2: 'local:iso/isofile.iso,media=cdrom',
99 | sata0: 'raid:30,format=qcow2',
100 | sockets: 1,
101 | cores: 1,
102 | net0: 'e1000,bridge=vmbr0',
103 | memory: 256,
104 | description: 'vagrant_test_machine'})
105 | action.call env
106 | end
107 | end
108 |
109 | context 'with a specified hostname' do
110 | before { env[:machine].config.vm.hostname = 'hostname' }
111 | specify do
112 | expect(connection).to receive(:create_vm).
113 | with(node: 'localhost',
114 | vm_type: :qemu,
115 | params: {vmid: 100,
116 | name: 'hostname',
117 | ostype: :l26,
118 | ide2: 'local:iso/isofile.iso,media=cdrom',
119 | sata0: 'raid:30,format=qcow2',
120 | sockets: 1,
121 | cores: 1,
122 | net0: 'e1000,bridge=vmbr0',
123 | memory: 256,
124 | description: 'vagrant_test_machine'})
125 | action.call env
126 | end
127 | end
128 |
129 | context 'with predefined network settings' do
130 | before { allow(env[:machine].config.vm).to receive(:networks) { [[:public_network, {ip: '127.0.0.1', macaddress: 'aa:bb:cc:dd:ee:ff'}]] } }
131 | specify do
132 | expect(connection).to receive(:create_vm).
133 | with(node: 'localhost',
134 | vm_type: :qemu,
135 | params: {vmid: 100,
136 | name: 'machine',
137 | ostype: :l26,
138 | ide2: 'local:iso/isofile.iso,media=cdrom',
139 | sata0: 'raid:30,format=qcow2',
140 | sockets: 1,
141 | cores: 1,
142 | net0: 'e1000=aa:bb:cc:dd:ee:ff,bridge=vmbr0',
143 | memory: 256,
144 | description: 'vagrant_test_machine'})
145 | action.call env
146 | end
147 | end
148 | end
149 | end
150 |
151 | it 'should print a message to the user interface' do
152 | expect(env[:ui]).to receive(:info).with 'Creating the virtual machine...'
153 | expect(env[:ui]).to receive(:info).with 'Done!'
154 | action.call env
155 | end
156 |
157 | it 'should store the node and vmid in env[:machine].id' do
158 | action.call env
159 | expect(env[:machine].id).to eq('localhost/100')
160 | end
161 |
162 | it 'should get a free vm id from connection' do
163 | expect(connection).to receive(:get_free_vm_id)
164 | action.send :call, env
165 | end
166 |
167 | context 'when the proxmox server responds with an error to the create request' do
168 |
169 | context 'when the proxmox server replies with an internal server error to the delete_vm call' do
170 | it 'should raise a VMCreateError' do
171 | allow(connection).to receive(:create_vm).and_raise ApiError::ServerError
172 | expect { action.send :call, env }.to raise_error Errors::VMCreateError
173 | end
174 | end
175 |
176 | context 'when the proxmox server replies with an internal server error to the task status request' do
177 | it 'should raise a VMCreateError' do
178 | allow(connection).to receive(:create_vm).and_raise ApiError::ServerError
179 | expect { action.send :call, env }.to raise_error Errors::VMCreateError
180 | end
181 | end
182 |
183 | context 'when the proxmox server does not reply the task status request with OK' do
184 | it 'should raise a VMCreateError' do
185 | allow(connection).to receive_messages :create_vm => 'create vm error'
186 | expect { action.send :call, env }.to raise_error Errors::VMCreateError, /create vm error/
187 | end
188 | end
189 | end
190 | end
191 | end
192 | end
193 |
--------------------------------------------------------------------------------
/spec/actions/destroy_vm_action_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'actions/proxmox_action_shared'
3 |
4 | module VagrantPlugins::Proxmox
5 |
6 | describe Action::DestroyVm do
7 |
8 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
9 | let(:connection) { Connection.new 'https://proxmox.example.com/api2/json' }
10 | let(:env) { {machine: environment.machine(environment.primary_machine_name, :proxmox),
11 | ui: double('ui').as_null_object,
12 | proxmox_connection: connection} }
13 |
14 | subject(:action) { described_class.new(-> (_) {}, environment) }
15 |
16 | describe '#call' do
17 |
18 | before do
19 | env[:machine].id = 'localhost/100'
20 | allow(connection).to receive_messages :delete_vm => 'OK'
21 | end
22 |
23 | it_behaves_like 'a proxmox action call'
24 |
25 | it 'should call the delete_vm function of connection' do
26 | expect(connection).to receive(:delete_vm).with '100'
27 | action.call env
28 | end
29 |
30 | it 'should print a message to the user interface' do
31 | expect(env[:ui]).to receive(:info).with 'Destroying the virtual machine...'
32 | expect(env[:ui]).to receive(:info).with 'Done!'
33 | action.call env
34 | end
35 |
36 | context 'when the proxmox server responds with an error to the destroy request' do
37 |
38 | context 'when the proxmox server replies with an internal server error to the destroy request' do
39 | it 'should raise a VMDestroyError' do
40 | allow(connection).to receive(:delete_vm).and_raise ApiError::ServerError
41 | expect { action.send :call, env }.to raise_error Errors::VMDestroyError
42 | end
43 | end
44 |
45 | context 'when the proxmox server replies with an internal server error to the task status request' do
46 | it 'should raise a VMDestroyError' do
47 | allow(connection).to receive(:delete_vm).and_raise ApiError::ServerError
48 | expect { action.send :call, env }.to raise_error Errors::VMDestroyError
49 | end
50 | end
51 |
52 | context 'when the proxmox server does not reply the task status request with OK' do
53 | it 'should raise a VMDestroyError' do
54 | allow(connection).to receive_messages :delete_vm => 'destroy vm error'
55 | expect { action.send :call, env }.to raise_error Errors::VMDestroyError, /destroy vm error/
56 | end
57 | end
58 |
59 | end
60 |
61 | end
62 |
63 | end
64 | end
65 |
--------------------------------------------------------------------------------
/spec/actions/get_node_list_action_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'actions/proxmox_action_shared'
3 |
4 | module VagrantPlugins::Proxmox
5 |
6 | describe Action::GetNodeList do
7 |
8 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
9 | let(:connection) { Connection.new 'https://proxmox.example.com/api2/json' }
10 | let(:env) { {machine: environment.machine(environment.primary_machine_name, :proxmox), proxmox_connection: connection} }
11 |
12 | subject(:action) { described_class.new(-> (_) {}, environment) }
13 |
14 | describe '#call' do
15 |
16 | describe 'proxmox action call' do
17 | before { allow(connection).to receive_messages get_node_list: [] }
18 | it_behaves_like 'a proxmox action call'
19 | end
20 |
21 | it 'should store the node list in env[:proxmox_nodes]' do
22 | expect(connection).to receive(:get_node_list).and_return ['node1', 'node2']
23 | action.call env
24 | expect(env[:proxmox_nodes]).to eq(['node1', 'node2'])
25 | end
26 |
27 | context 'when the server communication fails' do
28 | before { allow(connection).to receive(:get_node_list).and_raise ApiError::ConnectionError }
29 | it 'should raise an error' do
30 | expect { action.call env }.to raise_error Errors::CommunicationError
31 | end
32 | end
33 |
34 | end
35 |
36 | end
37 |
38 | end
--------------------------------------------------------------------------------
/spec/actions/is_created_action_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'actions/proxmox_action_shared'
3 |
4 | describe VagrantPlugins::Proxmox::Action::IsCreated do
5 |
6 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
7 | let(:env) { {machine: environment.machine(environment.primary_machine_name, :proxmox)} }
8 |
9 | subject(:action) { described_class.new(-> (_) {}, environment) }
10 |
11 | describe '#call' do
12 |
13 | before { allow(env[:machine].provider).to receive_messages :state => Vagrant::MachineState.new(nil, nil, nil) }
14 |
15 | it_behaves_like 'a proxmox action call'
16 |
17 | context 'when the machine is stopped' do
18 | before do
19 | allow(env[:machine].provider).to receive_messages :state => Vagrant::MachineState.new(:stopped, '', '')
20 | action.call env
21 | end
22 | specify { expect(env[:result]).to eq(true) }
23 | end
24 |
25 | context 'when the machine is not created' do
26 | before do
27 | allow(env[:machine].provider).to receive_messages :state => Vagrant::MachineState.new(:not_created, '', '')
28 | action.call env
29 | end
30 | specify { expect(env[:result]).to eq(false) }
31 | end
32 |
33 | end
34 |
35 | end
36 |
--------------------------------------------------------------------------------
/spec/actions/is_stopped_action_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'actions/proxmox_action_shared'
3 |
4 | describe VagrantPlugins::Proxmox::Action::IsStopped do
5 |
6 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
7 | let(:env) { {machine: environment.machine(environment.primary_machine_name, :proxmox)} }
8 |
9 | subject(:action) { described_class.new(-> (_) {}, environment) }
10 |
11 | describe '#call' do
12 |
13 | before { allow(env[:machine].provider).to receive_messages :state => Vagrant::MachineState.new(nil, nil, nil) }
14 |
15 | it_behaves_like 'a proxmox action call'
16 |
17 | context 'when the machine is stopped' do
18 | before do
19 | allow(env[:machine].provider).to receive_messages :state => Vagrant::MachineState.new(:stopped, '', '')
20 | action.call env
21 | end
22 | specify { expect(env[:result]).to eq(true) }
23 | end
24 |
25 | context 'when the machine is running' do
26 | before do
27 | allow(env[:machine].provider).to receive_messages :state => Vagrant::MachineState.new(:running, '', '')
28 | action.call env
29 | end
30 | specify { expect(env[:result]).to eq(false) }
31 | end
32 | end
33 |
34 | end
35 |
--------------------------------------------------------------------------------
/spec/actions/message_already_running_action_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'actions/proxmox_action_shared'
3 |
4 | describe VagrantPlugins::Proxmox::Action::MessageAlreadyRunning do
5 |
6 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
7 | let(:env) { {ui: double('ui').as_null_object} }
8 |
9 | subject(:action) { described_class.new(-> (_) {}, environment) }
10 |
11 | before { VagrantPlugins::Proxmox::Plugin.setup_i18n }
12 |
13 | describe '#call' do
14 |
15 | it_behaves_like 'a proxmox action call'
16 |
17 | specify do
18 | expect(env[:ui]).to receive(:info).with 'The virtual machine is already up and running'
19 | action.call env
20 | end
21 | end
22 |
23 | end
24 |
--------------------------------------------------------------------------------
/spec/actions/message_already_stopped_action_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'actions/proxmox_action_shared'
3 |
4 | describe VagrantPlugins::Proxmox::Action::MessageAlreadyStopped do
5 |
6 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
7 | let(:env) { {ui: double('ui').as_null_object} }
8 |
9 | subject(:action) { described_class.new(-> (_) {}, environment) }
10 |
11 | before { VagrantPlugins::Proxmox::Plugin.setup_i18n }
12 |
13 | describe '#call' do
14 |
15 | it_behaves_like 'a proxmox action call'
16 |
17 | specify do
18 | expect(env[:ui]).to receive(:info).with 'The virtual machine is already stopped'
19 | action.call env
20 | end
21 | end
22 |
23 | end
24 |
--------------------------------------------------------------------------------
/spec/actions/message_file_not_found_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'actions/proxmox_action_shared'
3 |
4 | describe VagrantPlugins::Proxmox::Action::MessageFileNotFound do
5 |
6 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
7 | let(:env) { {ui: double('ui').as_null_object} }
8 |
9 | subject(:action) { described_class.new(-> (_) {}, environment) }
10 |
11 | before { VagrantPlugins::Proxmox::Plugin.setup_i18n }
12 |
13 | describe '#call' do
14 |
15 | it_behaves_like 'a proxmox action call'
16 |
17 | specify do
18 | expect(env[:ui]).to receive(:info).with /File for upload not found/
19 | action.call env
20 | end
21 | end
22 |
23 | end
24 |
--------------------------------------------------------------------------------
/spec/actions/message_not_created_action_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'actions/proxmox_action_shared'
3 |
4 | describe VagrantPlugins::Proxmox::Action::MessageNotCreated do
5 |
6 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
7 | let(:env) { {ui: double('ui').as_null_object} }
8 |
9 | subject(:action) { described_class.new(-> (_) {}, environment) }
10 |
11 | before { VagrantPlugins::Proxmox::Plugin.setup_i18n }
12 |
13 | describe '#call' do
14 |
15 | it_behaves_like 'a proxmox action call'
16 |
17 | specify do
18 | expect(env[:ui]).to receive(:info).with 'The virtual machine is not created on the server!'
19 | action.call env
20 | end
21 | end
22 |
23 | end
24 |
--------------------------------------------------------------------------------
/spec/actions/message_not_running_action_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'actions/proxmox_action_shared'
3 |
4 | describe VagrantPlugins::Proxmox::Action::MessageNotRunning do
5 |
6 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
7 | let(:env) { {ui: double('ui').as_null_object} }
8 |
9 | subject(:action) { described_class.new(-> (_) {}, environment) }
10 |
11 | before { VagrantPlugins::Proxmox::Plugin.setup_i18n }
12 |
13 | describe '#call' do
14 |
15 | it_behaves_like 'a proxmox action call'
16 |
17 | specify do
18 | expect(env[:ui]).to receive(:info).with /VM must be running to execute this command/
19 | action.call env
20 | end
21 | end
22 |
23 | end
24 |
--------------------------------------------------------------------------------
/spec/actions/message_upload_server_error_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'actions/proxmox_action_shared'
3 |
4 | describe VagrantPlugins::Proxmox::Action::MessageUploadServerError do
5 |
6 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
7 | let(:env) { {ui: double('ui').as_null_object} }
8 |
9 | subject(:action) { described_class.new(-> (_) {}, environment) }
10 |
11 | before { VagrantPlugins::Proxmox::Plugin.setup_i18n }
12 |
13 | describe '#call' do
14 |
15 | it_behaves_like 'a proxmox action call'
16 |
17 | specify do
18 | expect(env[:ui]).to receive(:info).with /Error during upload/
19 | action.call env
20 | end
21 | end
22 |
23 | end
24 |
--------------------------------------------------------------------------------
/spec/actions/proxmox_action_shared.rb:
--------------------------------------------------------------------------------
1 | shared_examples 'a proxmox action call' do
2 |
3 | describe 'when done' do
4 | it 'should call the next action' do
5 | expect(subject).to receive(:next_action).with env
6 | subject.call env
7 | end
8 | end
9 |
10 | end
11 |
--------------------------------------------------------------------------------
/spec/actions/proxmox_action_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | module VagrantPlugins::Proxmox
4 |
5 | describe Action::ProxmoxAction do
6 |
7 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
8 | let(:connection) { Connection.new 'https://proxmox.example.com/api2/json' }
9 | let(:env) { {machine: environment.machine(environment.primary_machine_name, :proxmox),
10 | proxmox_connection: connection} }
11 |
12 | let (:action) { subject }
13 |
14 | describe '#next_action' do
15 |
16 | let(:app) { double('app') }
17 | before { action.instance_variable_set(:@app, app) }
18 |
19 | it 'should call @app' do
20 | expect(app).to receive(:call)
21 | action.send(:next_action, env)
22 | end
23 | end
24 |
25 | describe '#get_machine_ip_address' do
26 |
27 | before do
28 | allow(env[:machine].config.vm).to receive_messages networks: [[:private_network, {ip: '4.3.2.1'}], [:public_network, {ip: '1.2.3.4'}]]
29 | end
30 |
31 | it 'should return the first public ip address from the configuration' do
32 | expect(action.send(:get_machine_ip_address, env)).to eq('1.2.3.4')
33 | end
34 |
35 | context 'no network configuration exists' do
36 |
37 | before do
38 | allow(env[:machine].config.vm).to receive_messages networks: nil
39 | end
40 |
41 | it 'should return nil' do
42 | expect(action.send(:get_machine_ip_address, env)).to be_nil
43 | end
44 |
45 | end
46 |
47 | end
48 |
49 | describe '#connection' do
50 | it 'should retrieve the connection from the environment' do
51 | expect(action.send(:connection, env)).to eq(connection)
52 | end
53 | end
54 |
55 | end
56 |
57 | end
--------------------------------------------------------------------------------
/spec/actions/read_ssh_info_action_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'actions/proxmox_action_shared'
3 |
4 | describe VagrantPlugins::Proxmox::Action::ReadSSHInfo do
5 |
6 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
7 | let(:env) { {machine: environment.machine(environment.primary_machine_name, :proxmox)} }
8 |
9 | subject(:action) { described_class.new(-> (_) {}, environment) }
10 |
11 | describe '#call' do
12 |
13 | it_behaves_like 'a proxmox action call'
14 |
15 | context 'when no ip address is configured' do
16 | it 'should write no ssh info into env[:machine_ssh_info]' do
17 | action.call env
18 | expect(env[:machine_ssh_info]).to eq(nil)
19 | end
20 | end
21 |
22 | context 'when an ip address is configured' do
23 | before { allow(env[:machine].config.vm).to receive(:networks) { [[:public_network, {ip: '127.0.0.1'}]] } }
24 | it 'should write the ssh info into env[:machine_ssh_info]' do
25 | action.call env
26 | expect(env[:machine_ssh_info]).to eq({host: '127.0.0.1', port: 22})
27 | end
28 | end
29 |
30 | end
31 |
32 | end
33 |
--------------------------------------------------------------------------------
/spec/actions/read_state_action_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'actions/proxmox_action_shared'
3 |
4 | module VagrantPlugins::Proxmox
5 |
6 | describe VagrantPlugins::Proxmox::Action::ReadState do
7 |
8 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
9 | let(:connection) { Connection.new 'https://proxmox.example.com/api2/json' }
10 | let(:env) { {machine: environment.machine(environment.primary_machine_name, :proxmox), proxmox_connection: connection} }
11 | let(:node) { 'localhost' }
12 | let(:vm_id) { '100' }
13 | let(:machine_id) { "#{node}/#{vm_id}" }
14 |
15 | subject(:action) { described_class.new(-> (_) {}, environment) }
16 |
17 | before do
18 | allow(env[:machine]).to receive(:id) { machine_id }
19 | allow(connection).to receive :get_vm_state
20 | end
21 |
22 | describe '#call' do
23 |
24 | it_behaves_like 'a proxmox action call'
25 |
26 | it 'should store the machine state in env[:machine_state_id]' do
27 | expect(connection).to receive(:get_vm_state).and_return :running
28 | action.call env
29 | expect(env[:machine_state_id]).to eq(:running)
30 | end
31 |
32 | it 'should call get_vm_state with the node and vm_id' do
33 | expect(connection).to receive(:get_vm_state).with vm_id
34 | action.call env
35 | end
36 |
37 | context 'when no machine id is defined' do
38 | let(:machine_id) { nil }
39 | it 'should [:machine_state_id] to :not_created' do
40 | action.call env
41 | expect(env[:machine_state_id]).to eq(:not_created)
42 | end
43 | end
44 |
45 | context 'when the server communication fails' do
46 | before { allow(connection).to receive(:get_vm_state).and_raise ApiError::ConnectionError }
47 | it 'should raise an error' do
48 | expect { action.call env }.to raise_error Errors::CommunicationError
49 | end
50 | end
51 |
52 | end
53 |
54 | end
55 |
56 | end
57 |
--------------------------------------------------------------------------------
/spec/actions/select_node_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'actions/proxmox_action_shared'
3 |
4 | module VagrantPlugins::Proxmox
5 |
6 | describe VagrantPlugins::Proxmox::Action::SelectNode do
7 |
8 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
9 | let(:connection) { Connection.new 'https://proxmox.example.com/api2/json' }
10 | let(:env) { {machine: environment.machine(environment.primary_machine_name, :proxmox),
11 | proxmox_connection: connection, proxmox_nodes: nodes} }
12 | let(:nodes) { ['node1', 'node2', 'selected_node'] }
13 |
14 | subject(:action) { described_class.new(-> (_) {}, environment) }
15 |
16 | describe '#call' do
17 |
18 | it_behaves_like 'a proxmox action call'
19 |
20 | context "when no selected_node is specified in the configuration" do
21 | it 'randomly selects a node from the list of available nodes' do
22 | expect(nodes).to receive(:sample).and_return 'node2'
23 | action.call env
24 | expect(env[:proxmox_selected_node]).to eq('node2')
25 | end
26 | end
27 |
28 | context "when a specific node is specified in the configuration" do
29 |
30 | context "when this node is included in the nodes list" do
31 | before do
32 | env[:machine].provider_config.selected_node = 'selected_node'
33 | end
34 |
35 | it 'selects the selected_node' do
36 | expect(nodes).not_to receive(:sample)
37 | action.call env
38 | expect(env[:proxmox_selected_node]).to eq('selected_node')
39 | end
40 | end
41 |
42 | context "when this node is not included in the nodes list" do
43 | before do
44 | env[:machine].provider_config.selected_node = 'invalid_node'
45 | end
46 |
47 | it 'should raise an error' do
48 | expect { action.call env }.to raise_error Errors::InvalidNodeError
49 | end
50 | end
51 | end
52 |
53 | end
54 |
55 | end
56 |
57 | end
58 |
--------------------------------------------------------------------------------
/spec/actions/shutdown_vm_action_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'actions/proxmox_action_shared'
3 |
4 | module VagrantPlugins::Proxmox
5 |
6 | describe Action::ShutdownVm do
7 |
8 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
9 | let(:connection) { Connection.new 'https://proxmox.example.com/api2/json' }
10 | let(:env) { {machine: environment.machine(environment.primary_machine_name, :proxmox),
11 | ui: double('ui').as_null_object,
12 | proxmox_connection: connection} }
13 |
14 | subject(:action) { described_class.new(-> (_) {}, environment) }
15 |
16 | describe '#call' do
17 |
18 | before do
19 | env[:machine].id = 'localhost/100'
20 | allow(connection).to receive_messages :shutdown_vm => 'OK'
21 | end
22 |
23 | it_behaves_like 'a proxmox action call'
24 |
25 | it 'should call the shutdown_vm function of connection' do
26 | expect(connection).to receive(:shutdown_vm).with '100'
27 | action.call env
28 | end
29 |
30 | it 'should print a message to the user interface' do
31 | expect(env[:ui]).to receive(:info).with 'Shutting down the virtual machine...'
32 | expect(env[:ui]).to receive(:info).with 'Done!'
33 | action.call env
34 | end
35 |
36 | context 'when the proxmox server responds with an error to the shutdown request' do
37 |
38 | context 'when the proxmox server replies with an internal server error to the shutdown request' do
39 | it 'should raise a VMShutdownError' do
40 | allow(connection).to receive(:shutdown_vm).and_raise ApiError::ServerError
41 | expect { action.send :call, env }.to raise_error Errors::VMShutdownError
42 | end
43 | end
44 |
45 | context 'when the proxmox server replies with an internal server error to the task status request' do
46 | it 'should raise a VMShutdownError' do
47 | allow(connection).to receive(:shutdown_vm).and_raise ApiError::ServerError
48 | expect { action.send :call, env }.to raise_error Errors::VMShutdownError
49 | end
50 | end
51 |
52 | context 'when the proxmox server does not reply the task status request with OK' do
53 | it 'should raise a VMShutdownError' do
54 | allow(connection).to receive_messages :shutdown_vm => 'shutdown vm error'
55 | expect { action.send :call, env }.to raise_error Errors::VMShutdownError, /shutdown vm error/
56 | end
57 | end
58 |
59 | end
60 |
61 | end
62 |
63 | end
64 |
65 | end
66 |
--------------------------------------------------------------------------------
/spec/actions/start_vm_action_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'actions/proxmox_action_shared'
3 |
4 | module VagrantPlugins::Proxmox
5 |
6 | describe Action::StartVm do
7 |
8 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
9 | let(:connection) { Connection.new 'https://proxmox.example.com/api2/json' }
10 | let(:env) { {machine: environment.machine(environment.primary_machine_name, :proxmox),
11 | ui: double('ui').as_null_object,
12 | proxmox_connection: connection} }
13 | let(:ssh_reachable) { true }
14 | let(:ssh_timeout) { 60 }
15 | let(:ssh_status_check_interval) { 5 }
16 |
17 | subject(:action) { described_class.new(-> (_) {}, environment) }
18 |
19 | describe '#call' do
20 |
21 | before do
22 | env[:machine].id = 'localhost/100'
23 | allow(connection).to receive_messages :start_vm => 'OK'
24 | allow(env[:machine].communicate).to receive_messages :ready? => ssh_reachable
25 | end
26 |
27 | it_behaves_like 'a proxmox action call'
28 |
29 | it 'should call the start_vm function of connection' do
30 | expect(connection).to receive(:start_vm).with '100'
31 | action.call env
32 | end
33 |
34 | it 'should print a message to the user interface' do
35 | expect(env[:ui]).to receive(:info).with 'Starting the virtual machine...'
36 | expect(env[:ui]).to receive(:info).with 'Done!'
37 | expect(env[:ui]).to receive(:info).with 'Waiting for SSH connection...'
38 | expect(env[:ui]).to receive(:info).with 'Done!'
39 | action.call env
40 | end
41 |
42 | it 'should periodically call env[:machine].communicate.ready? to check for ssh access' do
43 | expect(env[:machine].communicate).to receive(:ready?).and_return false
44 | expect(subject).to receive(:sleep).with ssh_status_check_interval
45 | expect(env[:machine].communicate).to receive(:ready?).and_return true
46 | action.call env
47 | end
48 |
49 | context 'when the proxmox server responds with an error to the start request' do
50 |
51 | context 'when the proxmox server replies with an internal server error to the start request' do
52 | it 'should raise a VMStartError' do
53 | allow(connection).to receive(:start_vm).and_raise ApiError::ServerError
54 | expect { action.send :call, env }.to raise_error Errors::VMStartError
55 | end
56 | end
57 |
58 | context 'when the proxmox server replies with an internal server error to the task status request' do
59 | it 'should raise a VMStartError' do
60 | allow(connection).to receive(:start_vm).and_raise ApiError::ServerError
61 | expect { action.send :call, env }.to raise_error Errors::VMStartError
62 | end
63 | end
64 |
65 | context 'when the proxmox server does not reply the task status request with OK' do
66 | it 'should raise a VMStartError' do
67 | allow(connection).to receive_messages :start_vm => 'start vm error'
68 | expect { action.send :call, env }.to raise_error Errors::VMStartError, /start vm error/
69 | end
70 | end
71 |
72 | end
73 |
74 | context 'when no ssh connection can be established after startup' do
75 |
76 | let(:ssh_reachable) { false }
77 |
78 | before do
79 | allow(action).to receive(:sleep) { |duration| Timecop.travel(Time.now + duration) }
80 | Timecop.freeze
81 | end
82 |
83 | after do
84 | Timecop.return
85 | end
86 |
87 | it 'should wait the default timeout' do
88 | begin
89 | action.call env
90 | rescue Errors::SSHError
91 | end
92 | expect(Time).to have_elapsed ssh_timeout.seconds
93 | end
94 |
95 | it 'should raise an ssh error' do
96 | expect { action.send :call, env }.to raise_error Errors::SSHError, /Unable to establish an ssh connection/
97 | end
98 | end
99 | end
100 | end
101 | end
102 |
--------------------------------------------------------------------------------
/spec/actions/stop_vm_action_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'actions/proxmox_action_shared'
3 |
4 | module VagrantPlugins::Proxmox
5 |
6 | describe Action::StopVm do
7 |
8 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
9 | let(:connection) { Connection.new 'https://proxmox.example.com/api2/json' }
10 | let(:env) { {machine: environment.machine(environment.primary_machine_name, :proxmox),
11 | ui: double('ui').as_null_object, proxmox_connection: connection} }
12 |
13 | subject(:action) { described_class.new(-> (_) {}, environment) }
14 |
15 | describe '#call' do
16 |
17 | before do
18 | env[:machine].id = 'localhost/100'
19 | allow(connection).to receive_messages :stop_vm => 'OK'
20 | end
21 |
22 | it_behaves_like 'a proxmox action call'
23 |
24 | it 'should call the stop_vm function of connection' do
25 | expect(connection).to receive(:stop_vm).with '100'
26 | action.call env
27 | end
28 |
29 | it 'should print a message to the user interface' do
30 | expect(env[:ui]).to receive(:info).with 'Stopping the virtual machine...'
31 | expect(env[:ui]).to receive(:info).with 'Done!'
32 | action.call env
33 | end
34 |
35 | context 'when the proxmox server responds with an error to the stop request' do
36 |
37 | context 'when the proxmox server replies with an internal server error to the stop request' do
38 | it 'should raise a VMStopError' do
39 | allow(connection).to receive(:stop_vm).and_raise ApiError::ServerError
40 | expect { action.send :call, env }.to raise_error Errors::VMStopError
41 | end
42 | end
43 |
44 | context 'when the proxmox server replies with an internal server error to the task status request' do
45 | it 'should raise a VMStopError' do
46 | allow(connection).to receive(:stop_vm).and_raise ApiError::ServerError
47 | expect { action.send :call, env }.to raise_error Errors::VMStopError
48 | end
49 | end
50 |
51 | context 'when the proxmox server does not reply the task status request with OK' do
52 | it 'should raise a VMStopError' do
53 | allow(connection).to receive_messages :stop_vm => 'stop vm error'
54 | expect { action.send :call, env }.to raise_error Errors::VMStopError, /stop vm error/
55 | end
56 | end
57 |
58 | end
59 |
60 | end
61 |
62 | end
63 |
64 | end
--------------------------------------------------------------------------------
/spec/actions/sync_folders_action_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'actions/proxmox_action_shared'
3 |
4 | module VagrantPlugins::Proxmox
5 |
6 | describe Action::SyncFolders do
7 |
8 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
9 | let(:env) { {machine: environment.machine(environment.primary_machine_name, :proxmox), ui: double('ui').as_null_object} }
10 |
11 | subject(:action) { described_class.new(-> (_) {}, environment) }
12 |
13 | describe '#call' do
14 |
15 | before do
16 | allow(env[:machine]).to receive(:ssh_info) { {host: '127.0.0.1', port: 22, username: 'vagrant', private_key_path: ['key']} }
17 | allow(env[:machine].communicate).to receive :sudo
18 | allow(Vagrant::Util::Subprocess).to receive_messages :execute => Vagrant::Util::Subprocess::Result.new(0, nil, nil)
19 | end
20 |
21 | it_behaves_like 'a proxmox action call'
22 |
23 | it 'should print a message to the user interface' do
24 | expect(env[:ui]).to receive(:info).with("Rsyncing folder: #{Dir.pwd}/ => /vagrant")
25 | action.call env
26 | end
27 |
28 | it 'should create a directory on the vm with the predefined ownership' do
29 | expect(env[:machine].communicate).to receive(:sudo).with("mkdir -p '/vagrant'")
30 | expect(env[:machine].communicate).to receive(:sudo).with("chown vagrant '/vagrant'")
31 | action.call env
32 | end
33 |
34 | it 'should rsync the directory with the vm' do
35 | expect(Vagrant::Util::Subprocess).to receive(:execute).with(
36 | 'rsync', '--verbose', '--archive', '--compress', '--delete',
37 | '-e', "ssh -p 22 -i 'key' -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null",
38 | "#{Dir.pwd}/", 'vagrant@127.0.0.1:/vagrant')
39 | action.call env
40 | end
41 |
42 | it 'should raise an error if the rsync fails' do
43 | allow(Vagrant::Util::Subprocess).to receive_messages :execute => Vagrant::Util::Subprocess::Result.new(1, nil, nil)
44 | expect { action.call(env) }.to raise_error Errors::RsyncError
45 | end
46 |
47 | end
48 |
49 | end
50 |
51 | end
52 |
--------------------------------------------------------------------------------
/spec/actions/upload_iso_file_action_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'actions/proxmox_action_shared'
3 |
4 | module VagrantPlugins::Proxmox
5 |
6 | describe Action::UploadIsoFile do
7 |
8 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile_qemu' }
9 | let(:connection) { Connection.new 'https://your.proxmox.server/api2/json' }
10 | let(:env) { {machine: environment.machine(environment.primary_machine_name, :proxmox),
11 | proxmox_connection: connection, proxmox_selected_node: node} }
12 | let(:iso_file) { 'some_iso.iso' }
13 | let(:iso_file_exists) { true }
14 | let(:replace_iso_file) { false }
15 | let(:node) { 'node1' }
16 |
17 | subject(:action) { described_class.new(-> (_) {}, environment) }
18 |
19 | before do
20 | env[:machine].provider_config.qemu_iso_file = iso_file
21 | env[:machine].provider_config.replace_qemu_iso_file = replace_iso_file
22 | allow(File).to receive(:exist?).with(iso_file).and_return(iso_file_exists)
23 | end
24 |
25 | context 'with a specified iso file' do
26 |
27 | it 'should upload the iso file into the local storage of the selected node' do
28 | expect(connection).to receive(:upload_file).with(iso_file, content_type: 'iso', node: node, storage: 'local', replace: replace_iso_file)
29 | action.call env
30 | end
31 | end
32 |
33 | it 'should return :ok after a successful upload' do
34 | allow(connection).to receive(:upload_file).with(iso_file, content_type: 'iso', node: node, storage: 'local', replace: replace_iso_file)
35 | action.call env
36 | expect(env[:result]).to eq(:ok)
37 | end
38 |
39 | context 'The iso file should be replaced' do
40 |
41 | let(:replace_iso_file) { true }
42 |
43 | it 'should delete the iso file on the server' do
44 | expect(connection).to receive(:delete_file).with(filename: iso_file, content_type: 'iso', node: node, storage: 'local')
45 | action.call env
46 | end
47 | end
48 |
49 | context 'when a server error occurs' do
50 |
51 | before do
52 | allow(connection).to receive(:upload_file).and_raise ApiError::ServerError
53 | end
54 |
55 | it 'should return :server_error' do
56 | action.call env
57 | expect(env[:result]).to eq(:server_upload_error)
58 | end
59 | end
60 |
61 | context 'without a specified iso file' do
62 |
63 | let(:iso_file) { nil }
64 |
65 | it 'does nothing and returns OK' do
66 | expect(connection).not_to receive(:upload_file)
67 | action.call env
68 | expect(env[:result]).to eq(:ok)
69 | end
70 | end
71 |
72 | context 'the specified iso file does not exist' do
73 |
74 | let (:iso_file_exists) { false }
75 |
76 | it 'should return :file_not_found' do
77 | action.call env
78 | expect(env[:result]).to eq(:file_not_found)
79 | end
80 | end
81 | end
82 | end
83 |
--------------------------------------------------------------------------------
/spec/actions/upload_template_file_action_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'actions/proxmox_action_shared'
3 |
4 | module VagrantPlugins::Proxmox
5 |
6 | describe Action::UploadTemplateFile do
7 |
8 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
9 | let(:connection) { Connection.new 'https://your.proxmox.server/api2/json' }
10 | let(:env) { {machine: environment.machine(environment.primary_machine_name, :proxmox),
11 | proxmox_connection: connection, proxmox_selected_node: node} }
12 | let(:template_file) { 'template.tar.gz' }
13 | let(:template_file_exists) { true }
14 | let(:replace_template_file) { false }
15 | let(:node) { 'node1' }
16 |
17 | subject(:action) { described_class.new(-> (_) {}, environment) }
18 |
19 | before do
20 | env[:machine].provider_config.openvz_template_file = template_file
21 | env[:machine].provider_config.replace_openvz_template_file = replace_template_file
22 | allow(File).to receive(:exist?).with(template_file).and_return(template_file_exists)
23 | end
24 |
25 | context 'with a specified template file' do
26 |
27 | it 'should upload the template file into the local storage of the selected node' do
28 | expect(connection).to receive(:upload_file).with(template_file, content_type: 'vztmpl', node: node, storage: 'local', replace: replace_template_file)
29 | action.call env
30 | end
31 | end
32 |
33 | it 'should return :ok after a successful upload' do
34 | allow(connection).to receive(:upload_file).with(template_file, content_type: 'vztmpl', node: node, storage: 'local', replace: replace_template_file)
35 | action.call env
36 | expect(env[:result]).to eq(:ok)
37 | end
38 |
39 | context 'the template file should be replaced' do
40 |
41 | let(:replace_template_file) { true }
42 |
43 | it 'should delete the template file on the server' do
44 | expect(connection).to receive(:delete_file).with(filename: template_file, content_type: 'vztmpl', node: node, storage: 'local')
45 | action.call env
46 | end
47 | end
48 |
49 | context 'when a server error occurs' do
50 |
51 | before do
52 | allow(connection).to receive(:upload_file).and_raise ApiError::ServerError
53 | end
54 |
55 | it 'should return :server_error' do
56 | action.call env
57 | expect(env[:result]).to eq(:server_upload_error)
58 | end
59 | end
60 |
61 | context 'without a specified template file' do
62 |
63 | let(:template_file) { nil }
64 |
65 | it 'does nothing and returns OK' do
66 | expect(connection).not_to receive(:upload_file)
67 | action.call env
68 | expect(env[:result]).to eq(:ok)
69 | end
70 | end
71 |
72 | context 'the specified template file does not exist' do
73 |
74 | let (:template_file_exists) { false }
75 |
76 | it 'should return :file_not_found' do
77 | action.call env
78 | expect(env[:result]).to eq(:file_not_found)
79 | end
80 | end
81 | end
82 | end
83 |
--------------------------------------------------------------------------------
/spec/commands/destroy_command_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | module VagrantPlugins::Proxmox
4 |
5 | describe 'Vagrant Proxmox provider destroy command', :need_box do
6 |
7 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
8 | let(:env) { {machine: environment.machine(environment.primary_machine_name, :proxmox)} }
9 | let(:ui) { double('ui').as_null_object }
10 |
11 | context 'the vm is not created on the proxmox server' do
12 | it 'should call the appropriate actions to print a ui message that the vm is not created' do
13 | expect(Action::ConnectProxmox).to be_called
14 | expect(Action::IsCreated).to be_called { |env| env[:result] = false }
15 | expect(Action::MessageNotCreated).to be_called
16 | expect(Action::DestroyVm).to be_omitted
17 | execute_vagrant_command environment, :destroy
18 | end
19 | end
20 |
21 | context 'the vm exists on the proxmox server' do
22 |
23 | context 'the destroy command is not confirmed' do
24 | it 'should call the appropriate actions to print a ui message that the vm will not be destroyed' do
25 | expect(Action::ConnectProxmox).to be_called
26 | expect(Action::IsCreated).to be_called { |env| env[:result] = true }
27 | expect(::Vagrant::Action::Builtin::DestroyConfirm).to be_called { |env| env[:result] = false }
28 | expect(::VagrantPlugins::ProviderVirtualBox::Action::MessageWillNotDestroy).to be_called
29 | expect(Action::DestroyVm).to be_omitted
30 | execute_vagrant_command environment, :destroy
31 | end
32 | end
33 |
34 | context 'the destroy command is confirmed' do
35 | context 'the vm is running' do
36 | it 'should call the appropriate actions to destroy the vm' do
37 | expect(Action::ConnectProxmox).to be_called
38 | expect(Action::IsCreated).to be_called { |env| env[:result] = true }
39 | expect(::Vagrant::Action::Builtin::DestroyConfirm).to be_called { |env| env[:result] = true }
40 | expect(Action::IsStopped).to be_called { |env| env[:result] = false }
41 | expect(Action::ShutdownVm).to be_called
42 | expect(Action::DestroyVm).to be_called
43 | expect(::Vagrant::Action::Builtin::ProvisionerCleanup).to be_called
44 | expect(Action::CleanupAfterDestroy).to be_called
45 | execute_vagrant_command environment, :destroy
46 | end
47 | end
48 |
49 | context 'the vm is stopped' do
50 | it 'should call the appropriate actions to destroy the vm' do
51 | expect(Action::ConnectProxmox).to be_called
52 | expect(Action::IsCreated).to be_called { |env| env[:result] = true }
53 | expect(Action::DestroyConfirm).to be_called { |env| env[:result] = true }
54 | expect(Action::IsStopped).to be_called { |env| env[:result] = true }
55 | expect(Action::DestroyVm).to be_called
56 | expect(::Vagrant::Action::Builtin::ProvisionerCleanup).to be_called
57 | expect(Action::CleanupAfterDestroy).to be_called
58 | expect(Action::ShutdownVm).to be_omitted
59 | execute_vagrant_command environment, :destroy
60 | end
61 | end
62 | end
63 |
64 | end
65 |
66 | end
67 |
68 | end
69 |
--------------------------------------------------------------------------------
/spec/commands/halt_command_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | module VagrantPlugins::Proxmox
4 |
5 | describe 'Vagrant Proxmox provider halt command', :need_box do
6 |
7 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
8 | let(:ui) { double('ui').as_null_object }
9 |
10 | context 'the vm is not created on the proxmox server' do
11 | it 'should call the appropriate actions and print a ui message that the vm is not created' do
12 | expect(Action::ConfigValidate).to be_called
13 | expect(Action::IsCreated).to be_called { |env| env[:result] = false }
14 | expect(Action::MessageNotCreated).to be_called
15 | expect(Action::ConnectProxmox).to be_omitted
16 | expect(Action::ShutdownVm).to be_omitted
17 | execute_vagrant_command environment, :halt
18 | end
19 | end
20 |
21 | context 'the vm is stopped' do
22 | it 'should not shut down the vm and print a vm message that the vm is already stopped' do
23 | expect(Action::ConfigValidate).to be_called
24 | expect(Action::IsCreated).to be_called { |env| env[:result] = true }
25 | expect(Action::IsStopped).to be_called { |env| env[:result] = true }
26 | expect(Action::MessageAlreadyStopped).to be_called
27 | expect(Action::ConnectProxmox).to be_omitted
28 | expect(Action::ShutdownVm).to be_omitted
29 | execute_vagrant_command environment, :halt
30 | end
31 | end
32 |
33 | context 'the vm is running' do
34 | it 'should call the appropriate actions to shut down the vm' do
35 | expect(Action::ConfigValidate).to be_called
36 | expect(Action::IsCreated).to be_called { |env| env[:result] = true }
37 | expect(Action::IsStopped).to be_called { |env| env[:result] = false }
38 | expect(Action::ConnectProxmox).to be_called
39 | expect(Action::ShutdownVm).to be_called
40 | execute_vagrant_command environment, :halt
41 | end
42 | end
43 |
44 | end
45 |
46 | end
47 |
--------------------------------------------------------------------------------
/spec/commands/provision_command_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | module VagrantPlugins::Proxmox
4 |
5 | describe 'Vagrant Proxmox provider provision command', :need_box do
6 |
7 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
8 | let(:ui) { double('ui').as_null_object }
9 |
10 | context 'the vm is not created on the proxmox server' do
11 | it 'should call the appropriate actions and print a ui message that the vm is not created' do
12 | expect(Action::ConfigValidate).to be_called
13 | expect(Action::IsCreated).to be_called { |env| env[:result] = false }
14 | expect(Action::MessageNotCreated).to be_called
15 | expect(Action::Provision).to be_omitted
16 | expect(Action::SyncFolders).to be_omitted
17 | execute_vagrant_command environment, :provision
18 | end
19 | end
20 |
21 | context 'the vm exists on the proxmox server' do
22 | it 'should call the appropriate actions and provision the vm' do
23 | expect(Action::ConfigValidate).to be_called
24 | expect(Action::IsCreated).to be_called { |env| env[:result] = true }
25 | expect(Action::IsStopped).to be_called { |env| env[:result] = false }
26 | expect(Action::Provision).to be_called
27 | expect(Action::SyncFolders).to be_called
28 | execute_vagrant_command environment, :provision
29 | end
30 | end
31 |
32 | context 'the vm exists on the proxmox server but is not running' do
33 |
34 | it 'should state that the machine is not currently running' do
35 | expect(Action::IsCreated).to be_called { |env| env[:result] = true }
36 | expect(Action::IsStopped).to be_called { |env| env[:result] = true }
37 | expect(Action::MessageNotRunning).to be_called
38 | expect(Action::Provision).to be_omitted
39 | execute_vagrant_command environment, :provision
40 | end
41 |
42 | end
43 |
44 | end
45 |
46 | end
47 |
--------------------------------------------------------------------------------
/spec/commands/ssh_command_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | module VagrantPlugins::Proxmox
4 |
5 | describe 'Vagrant Proxmox provider ssh command', :need_box do
6 |
7 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
8 | let(:ui) { double('ui').as_null_object }
9 |
10 | context 'the vm is not created on the proxmox server' do
11 | it 'should call the appropriate actions and print a ui message that the vm is not created' do
12 | expect(Action::IsCreated).to be_called { |env| env[:result] = false }
13 | expect(Action::MessageNotCreated).to be_called
14 | expect(Action::SSHExec).to be_omitted
15 | execute_vagrant_command environment, :ssh
16 | end
17 | end
18 |
19 | context 'the vm exists on the proxmox server' do
20 |
21 | it 'should call the appropriate actions to open a ssh connection' do
22 | expect(Action::IsCreated).to be_called { |env| env[:result] = true }
23 | expect(Action::IsStopped).to be_called { |env| env[:result] = false }
24 | expect(Action::SSHExec).to be_called
25 | execute_vagrant_command environment, :ssh
26 | end
27 |
28 | end
29 |
30 | context 'the vm exists on the proxmox server but is not running' do
31 |
32 | it 'should state that the machine is not currently running' do
33 | expect(Action::IsCreated).to be_called { |env| env[:result] = true }
34 | expect(Action::IsStopped).to be_called { |env| env[:result] = true }
35 | expect(Action::MessageNotRunning).to be_called
36 | expect(Action::SSHExec).to be_omitted
37 | execute_vagrant_command environment, :ssh
38 | end
39 |
40 | end
41 |
42 | end
43 |
44 | end
45 |
--------------------------------------------------------------------------------
/spec/commands/ssh_run_command_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | module VagrantPlugins::Proxmox
4 |
5 | describe 'Vagrant Proxmox provider ssh exec command', :need_box do
6 |
7 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
8 | let(:env) { {machine: environment.machine(environment.primary_machine_name, :proxmox), ui: double('ui').as_null_object} }
9 | let(:ui) { double('ui').as_null_object }
10 |
11 | context 'the vm is not created on the proxmox server' do
12 | it 'should call the appropriate actions and print a ui message that the vm is not created' do
13 | expect(Action::IsCreated).to be_called { |env| env[:result] = false }
14 | expect(Action::MessageNotCreated).to be_called
15 | expect(Action::SSHRun).to be_omitted
16 | execute_vagrant_command environment, :ssh, '--command', 'foo'
17 | end
18 | end
19 |
20 | context 'the vm exists on the proxmox server' do
21 | before { allow(env[:machine]).to receive(:ssh_info) { {host: '127.0.0.1', port: 22, username: 'vagrant', private_key_path: 'key'} } }
22 |
23 | it 'should call the appropriate actions to execute a command via ssh on the vm' do
24 | expect(Action::IsCreated).to be_called { |env| env[:result] = true }
25 | expect(Action::IsStopped).to be_called { |env| env[:result] = false }
26 | expect(Action::SSHRun).to be_called
27 | execute_vagrant_command environment, :ssh, '--command', 'foo'
28 | end
29 | end
30 |
31 | context 'the vm exists on the proxmox server and is not running' do
32 | before { allow(env[:machine]).to receive(:ssh_info) { {host: '127.0.0.1', port: 22, username: 'vagrant', private_key_path: 'key'} } }
33 |
34 | it 'should call the appropriate actions to execute a command via ssh on the vm' do
35 | expect(Action::IsCreated).to be_called { |env| env[:result] = true }
36 | expect(Action::IsStopped).to be_called { |env| env[:result] = true }
37 | expect(Action::MessageNotRunning).to be_called
38 | expect(Action::SSHRun).to be_omitted
39 | execute_vagrant_command environment, :ssh, '--command', 'foo'
40 | end
41 | end
42 |
43 | end
44 |
45 | end
46 |
--------------------------------------------------------------------------------
/spec/commands/status_command_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | module VagrantPlugins::Proxmox
4 |
5 | describe 'Vagrant Proxmox provider status command', :need_box do
6 |
7 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
8 | let(:ui) { double('ui').as_null_object }
9 |
10 | specify do
11 | expect(Action::ConnectProxmox).to be_called
12 | expect(Action::ReadState).to be_called { |env| env[:machine_state_id] = :stopped }
13 | expect(ui).to receive(:info).with /stopped \(proxmox\)/, anything
14 | execute_vagrant_command environment, :status
15 | end
16 |
17 | end
18 |
19 | end
20 |
--------------------------------------------------------------------------------
/spec/commands/up_command_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | module VagrantPlugins::Proxmox
4 |
5 | describe 'Vagrant Proxmox provider up command' do
6 |
7 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
8 | let(:connection) { Connection.new 'https://your.proxmox.server/api2/json' }
9 | let(:env) { {machine: environment.machine(environment.primary_machine_name, :proxmox)} }
10 | let(:ui) { double('ui').as_null_object }
11 | let(:proxmox_vm_type) { :openvz }
12 | let(:qemu_os) { :l26 }
13 | let(:qemu_iso) { 'someiso.iso' }
14 | let(:qemu_disk_size) { '30G' }
15 |
16 | before do
17 | allow(Vagrant::UI::Interface).to receive_messages :new => ui
18 | env[:machine].provider_config.vm_type = proxmox_vm_type
19 | end
20 |
21 | context 'the vm_type is :openvz' do
22 |
23 | context 'the vm is not yet created' do
24 | it 'should call the appropriate actions and print a ui message that the vm will be created' do
25 | expect(Action::ConnectProxmox).to be_called
26 | expect(Action::GetNodeList).to be_called { |env| env[:proxmox_nodes] = ['localhost'] }
27 | expect(Action::SelectNode).to be_called { |env| env[:proxmox_selected_node] = ['localhost'] }
28 | expect(Action::IsCreated).to be_called { |env| env[:result] = false }
29 | expect(Action::UploadTemplateFile).to be_called { |env| env[:result] = :ok }
30 | expect(Action::UploadIsoFile).to be_omitted
31 | expect(Action::CreateVm).to be_called { |env| env[:machine].id = 'localhost/100' }
32 | expect(Action::Provision).to be_called
33 | expect(Action::StartVm).to be_called
34 | expect(Action::SyncFolders).to be_called
35 | execute_vagrant_command environment, :up, '--provider=proxmox'
36 | end
37 | end
38 |
39 | context 'the vm is stopped' do
40 | it 'should call the appropriate actions and print a ui message that the vm will be started' do
41 | expect(Action::ConnectProxmox).to be_called
42 | expect(Action::IsCreated).to be_called { |env| env[:result] = true }
43 | expect(Action::IsStopped).to be_called { |env| env[:result] = true }
44 | expect(Action::Provision).to be_called
45 | expect(Action::StartVm).to be_called
46 | expect(Action::SyncFolders).to be_called
47 | expect(Action::UploadTemplateFile).to be_omitted
48 | expect(Action::UploadIsoFile).to be_omitted
49 | expect(Action::CreateVm).to be_omitted
50 | execute_vagrant_command environment, :up, '--provider=proxmox'
51 | end
52 | end
53 |
54 | context 'the vm is already running' do
55 | it 'should call the appropriate actions and print a ui message that the vm is already running' do
56 | expect(Action::ConnectProxmox).to be_called
57 | expect(Action::IsCreated).to be_called { |env| env[:result] = true }
58 | expect(Action::IsStopped).to be_called { |env| env[:result] = false }
59 | expect(Action::MessageAlreadyRunning).to be_called
60 | expect(Action::Provision).to be_omitted
61 | expect(Action::UploadTemplateFile).to be_omitted
62 | expect(Action::UploadIsoFile).to be_omitted
63 | expect(Action::CreateVm).to be_omitted
64 | expect(Action::StartVm).to be_omitted
65 | execute_vagrant_command environment, :up, '--provider=proxmox'
66 | end
67 | end
68 |
69 | context 'an invalid local template file is specified' do
70 |
71 | it 'should call the appropriate actions and print a ui message that a file was not found' do
72 | expect(Action::ConnectProxmox).to be_called
73 | expect(Action::GetNodeList).to be_called { |env| env[:proxmox_nodes] = ['localhost'] }
74 | expect(Action::IsCreated).to be_called { |env| env[:result] = false }
75 | expect(Action::Provision).to be_called
76 | expect(Action::UploadTemplateFile).to be_called { |env| env[:result] = :file_not_found }
77 | expect(Action::UploadIsoFile).to be_omitted
78 | expect(Action::MessageFileNotFound).to be_called
79 | expect(Action::CreateVm).to be_omitted
80 | expect(Action::StartVm).to be_omitted
81 | expect(Action::SyncFolders).to be_omitted
82 | execute_vagrant_command environment, :up, '--provider=proxmox'
83 | end
84 | end
85 |
86 | context 'a server error occurs' do
87 |
88 | it 'should call the appropriate actions and print a ui message that a server error occured' do
89 | expect(Action::ConnectProxmox).to be_called
90 | expect(Action::GetNodeList).to be_called { |env| env[:proxmox_nodes] = ['localhost'] }
91 | expect(Action::IsCreated).to be_called { |env| env[:result] = false }
92 | expect(Action::Provision).to be_called
93 | expect(Action::UploadTemplateFile).to be_called { |env| env[:result] = :server_upload_error }
94 | expect(Action::UploadIsoFile).to be_omitted
95 | expect(Action::MessageUploadServerError).to be_called
96 | expect(Action::CreateVm).to be_omitted
97 | expect(Action::StartVm).to be_omitted
98 | expect(Action::SyncFolders).to be_omitted
99 | execute_vagrant_command environment, :up, '--provider=proxmox'
100 | end
101 | end
102 | end
103 |
104 | context 'the vm_type is :qemu' do
105 |
106 | let(:proxmox_vm_type) { :qemu }
107 |
108 | before do
109 | env[:machine].provider_config.qemu_os = qemu_os
110 | env[:machine].provider_config.qemu_iso = qemu_iso
111 | env[:machine].provider_config.qemu_disk_size = qemu_disk_size
112 | end
113 |
114 | context 'the vm is not yet created' do
115 | it 'should call the appropriate actions and print a ui message that the vm will be created' do
116 | expect(Action::ConnectProxmox).to be_called
117 | expect(Action::GetNodeList).to be_called { |env| env[:proxmox_nodes] = ['localhost'] }
118 | expect(Action::SelectNode).to be_called { |env| env[:proxmox_selected_node] = ['localhost'] }
119 | expect(Action::IsCreated).to be_called { |env| env[:result] = false }
120 | expect(Action::UploadTemplateFile).to be_omitted
121 | expect(Action::UploadIsoFile).to be_called { |env| env[:result] = :ok }
122 | expect(Action::CreateVm).to be_called { |env| env[:machine].id = 'localhost/100' }
123 | expect(Action::Provision).to be_called
124 | expect(Action::StartVm).to be_called
125 | expect(Action::SyncFolders).to be_called
126 | execute_vagrant_command environment, :up, '--provider=proxmox'
127 | end
128 | end
129 |
130 | context 'the vm is stopped' do
131 | it 'should call the appropriate actions and print a ui message that the vm will be started' do
132 | expect(Action::ConnectProxmox).to be_called
133 | expect(Action::IsCreated).to be_called { |env| env[:result] = true }
134 | expect(Action::IsStopped).to be_called { |env| env[:result] = true }
135 | expect(Action::Provision).to be_called
136 | expect(Action::StartVm).to be_called
137 | expect(Action::SyncFolders).to be_called
138 | expect(Action::UploadTemplateFile).to be_omitted
139 | expect(Action::UploadIsoFile).to be_omitted
140 | expect(Action::CreateVm).to be_omitted
141 | execute_vagrant_command environment, :up, '--provider=proxmox'
142 | end
143 | end
144 |
145 | context 'the vm is already running' do
146 | it 'should call the appropriate actions and print a ui message that the vm is already running' do
147 | expect(Action::ConnectProxmox).to be_called
148 | expect(Action::IsCreated).to be_called { |env| env[:result] = true }
149 | expect(Action::IsStopped).to be_called { |env| env[:result] = false }
150 | expect(Action::MessageAlreadyRunning).to be_called
151 | expect(Action::Provision).to be_omitted
152 | expect(Action::UploadTemplateFile).to be_omitted
153 | expect(Action::UploadIsoFile).to be_omitted
154 | expect(Action::CreateVm).to be_omitted
155 | expect(Action::StartVm).to be_omitted
156 | execute_vagrant_command environment, :up, '--provider=proxmox'
157 | end
158 | end
159 |
160 | context 'an invalid local iso file is specified' do
161 |
162 | it 'should call the appropriate actions and print a ui message that a file was not found' do
163 | expect(Action::ConnectProxmox).to be_called
164 | expect(Action::GetNodeList).to be_called { |env| env[:proxmox_nodes] = ['localhost'] }
165 | expect(Action::IsCreated).to be_called { |env| env[:result] = false }
166 | expect(Action::Provision).to be_called
167 | expect(Action::UploadTemplateFile).to be_omitted
168 | expect(Action::UploadIsoFile).to be_called { |env| env[:result] = :file_not_found }
169 | expect(Action::MessageFileNotFound).to be_called
170 | expect(Action::CreateVm).to be_omitted
171 | expect(Action::StartVm).to be_omitted
172 | expect(Action::SyncFolders).to be_omitted
173 | execute_vagrant_command environment, :up, '--provider=proxmox'
174 | end
175 | end
176 |
177 | context 'a server error occurs' do
178 |
179 | it 'should call the appropriate actions and print a ui message that a server error occured' do
180 | expect(Action::ConnectProxmox).to be_called
181 | expect(Action::GetNodeList).to be_called { |env| env[:proxmox_nodes] = ['localhost'] }
182 | expect(Action::IsCreated).to be_called { |env| env[:result] = false }
183 | expect(Action::Provision).to be_called
184 | expect(Action::UploadTemplateFile).to be_omitted
185 | expect(Action::UploadIsoFile).to be_called { |env| env[:result] = :server_upload_error }
186 | expect(Action::MessageUploadServerError).to be_called
187 | expect(Action::CreateVm).to be_omitted
188 | expect(Action::StartVm).to be_omitted
189 | expect(Action::SyncFolders).to be_omitted
190 | execute_vagrant_command environment, :up, '--provider=proxmox'
191 | end
192 | end
193 | end
194 | end
195 | end
196 |
--------------------------------------------------------------------------------
/spec/plugin_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe 'Vagrant Proxmox plugin' do
4 |
5 | specify { expect(Vagrant).to have_plugin 'Proxmox' }
6 |
7 | describe 'when vagrant log level is set in ENV[VAGRANT_LOG]' do
8 | before { ENV['VAGRANT_LOG'] = 'DEBUG' }
9 | it 'should create a new vagrant proxmox logger ' do
10 | expect(Log4r::Logger).to receive(:new).with('vagrant_proxmox').and_call_original
11 | VagrantPlugins::Proxmox::Plugin.setup_logging
12 | end
13 | end
14 |
15 | end
16 |
--------------------------------------------------------------------------------
/spec/provider_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'vagrant-proxmox/provider'
3 |
4 | module VagrantPlugins::Proxmox
5 |
6 | describe Provider do
7 |
8 | let(:machine) { environment.machine(environment.primary_machine_name, :proxmox) }
9 | let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
10 | let(:ui) { double('ui').as_null_object }
11 |
12 | subject { described_class.new(machine) }
13 |
14 | describe '#ssh_info', :need_box do
15 | it 'should call the appropriate actions and return the ssh info' do
16 | expect(Action::ConfigValidate).to be_called
17 | expect(Action::ConnectProxmox).to be_called
18 | expect(Action::GetNodeList).to be_called
19 | expect(Action::SelectNode).to be_called
20 | expect(Action::AdjustForwardedPortParams).to be_called
21 | expect(Action::ReadSSHInfo).to be_called { |env| env[:machine_ssh_info] = 'ssh_info' }
22 | expect(subject.ssh_info).to eq('ssh_info')
23 | end
24 | end
25 |
26 | end
27 |
28 | end
29 |
--------------------------------------------------------------------------------
/spec/proxmox/rest_call_shared.rb:
--------------------------------------------------------------------------------
1 | module VagrantPlugins::Proxmox
2 |
3 | shared_examples 'a rest api call' do |rest_method|
4 |
5 | context 'when an invalid resource is requested' do
6 | before { allow(RestClient).to receive(rest_method).and_raise RestClient::NotImplemented }
7 | it 'should raise a connection error' do
8 | expect do
9 | connection.send rest_method, '/invalid_resource'
10 | end.to raise_error ApiError::NotImplemented
11 | end
12 | end
13 |
14 | context 'when an internal server error occurs' do
15 | before { allow(RestClient).to receive(rest_method).and_raise RestClient::InternalServerError }
16 | it 'should raise a server error' do
17 | expect do
18 | connection.send rest_method, '/invalid_resource'
19 | end.to raise_error ApiError::ServerError
20 | end
21 | end
22 |
23 | context 'when a network error occurs' do
24 | before { allow(RestClient).to receive(rest_method).and_raise SocketError }
25 | it 'should raise a connection error' do
26 | expect do
27 | connection.send rest_method, '/resource'
28 | end.to raise_error ApiError::ConnectionError
29 | end
30 | end
31 |
32 | context 'when the client is not authorized' do
33 | before { allow(RestClient).to receive(rest_method).and_raise RestClient::Unauthorized }
34 | it 'should raise a unauthorized error' do
35 | expect do
36 | connection.send rest_method, "/resource"
37 | end.to raise_error ApiError::UnauthorizedError
38 | end
39 | end
40 | end
41 | end
--------------------------------------------------------------------------------
/spec/sanity_checks_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe 'Vagrant Proxmox sanity checks' do
4 |
5 | describe 'when loaded without vagrant installed' do
6 | before { allow_any_instance_of(Object).to receive(:require) { raise LoadError } }
7 | it 'should raise an error' do
8 | expect { load 'sanity_checks.rb' }.to raise_error
9 | end
10 | end
11 |
12 | describe 'when used with an outdated vagrant version' do
13 | before { stub_const 'Vagrant::VERSION', '1.0.7' }
14 | it 'should raise an error' do
15 | expect { load 'sanity_checks.rb' }.to raise_error
16 | end
17 | end
18 |
19 | end
20 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | if ENV['RUN_WITH_COVERAGE']
2 | require 'simplecov'
3 | require 'simplecov-rcov'
4 | SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
5 | SimpleCov::Formatter::HTMLFormatter,
6 | SimpleCov::Formatter::RcovFormatter,
7 | ]
8 | SimpleCov.start do
9 | add_filter '/dummy_box/Vagrantfile'
10 | add_filter '/spec/'
11 | add_filter '/lib/sanity_checks.rb'
12 | end
13 | end
14 |
15 | require 'vagrant-proxmox'
16 | require 'spec_helpers/common_helpers'
17 | require 'active_support/core_ext/string'
18 | require 'spec_helpers/time_helpers'
19 |
20 | RSpec.configure do |config|
21 |
22 | config.before(:suite) do
23 | add_dummy_box
24 | end
25 | config.after(:suite) do
26 | remove_dummy_box
27 | end
28 | config.after(:each) do
29 | FileUtils.rm_r '.vagrant', force: true
30 | end
31 |
32 | config.before(:each, :need_box) do
33 | up_local_box
34 | end
35 |
36 | end
37 |
38 | if ENV['COVERAGE']
39 | require 'simplecov'
40 | require 'simplecov-rcov'
41 | SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
42 | SimpleCov::Formatter::HTMLFormatter,
43 | SimpleCov::Formatter::RcovFormatter,
44 | ]
45 | SimpleCov.start
46 | end
--------------------------------------------------------------------------------
/spec/spec_helpers/common_helpers.rb:
--------------------------------------------------------------------------------
1 | def be_called &action_stub
2 | ActionCallMatcher.new action_stub, environment, self, :called
3 | end
4 |
5 | def be_omitted &action_stub
6 | ActionCallMatcher.new action_stub, environment, self, :omitted
7 | end
8 |
9 | # If you want to expect your actions to be called (and optionally stub their call methods),
10 | # use something like this:
11 | #
12 | # let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
13 | #
14 | # Action::ConnectProxmox.should be_called { |env| env[:proxmox_ticket] = 'ticket' }
15 | #
16 | class ActionCallMatcher < RSpec::Matchers::BuiltIn::BaseMatcher
17 | def initialize action_stub, environment, example_group, kind=:called
18 | super action_stub
19 | @environment = environment
20 | @example_group = example_group
21 | @kind = kind
22 | end
23 |
24 | def match(action_stub, action_class)
25 | @example_group.send(:mock_action, action_class).tap do |action|
26 | case @kind
27 | when :called
28 | @example_group.expect(action).to @example_group.receive(:call).at_least(:once) do |env|
29 | action_stub.call(env) if action_stub
30 | action.instance_variable_get(:@app).call env
31 | end
32 | when :omitted
33 | @example_group.expect(action).not_to @example_group.receive :call
34 | end
35 | end
36 | end
37 | end
38 |
39 | # If you want expectations on the action_class use something like:
40 | #
41 | # let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
42 | #
43 | # mock_action(VagrantPlugins::Proxmox::Action::ConnectProxmox).tap do |action|
44 | # action.should receive(:perform) {|env| env[:proxmox_ticket] = 'ticket' }
45 | # end
46 | #
47 | def mock_action action_class, env = RSpec::Mocks::Double.new('env').as_null_object
48 | action_class.new(nil, env).tap do |action_instance|
49 | action_instance_name = "@#{action_class.name.demodulize}Action".underscore
50 | self.instance_variable_set action_instance_name, action_instance
51 | allow(action_class).to receive(:new) do |app, _|
52 | action_instance.instance_variable_set '@app', app
53 | action_instance
54 | end
55 | end
56 | end
57 |
58 | # If you want to stub your actions (stub their call methods), use something like this:
59 | #
60 | # let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
61 | #
62 | # stub_action(Action::ConnectProxmox) { |env| env[:proxmox_ticket] = 'ticket' }
63 | #
64 | def stub_action action_class
65 | mock_action(action_class).tap do |action|
66 | allow(action).to receive(:call) do |env|
67 | yield env if block_given?
68 | action.instance_variable_get(:@app).call env
69 | end
70 | end
71 | end
72 |
73 | def unstub_action action_class
74 | RSpec::Mocks.space.proxy_for(action_class).reset
75 | end
76 |
77 | def execute_vagrant_command environment, command, *params
78 | Vagrant.plugin('2').manager.commands[command].new(params, environment).execute
79 | end
80 |
81 | def up_local_box
82 | allow(Vagrant::UI::Interface).to receive_messages :new => ui
83 | stub_action(VagrantPlugins::Proxmox::Action::ConnectProxmox)
84 | stub_action(VagrantPlugins::Proxmox::Action::GetNodeList) { |env| env[:proxmox_nodes] = [{node: 'node1'}] }
85 | stub_action(VagrantPlugins::Proxmox::Action::IsCreated) { |env| env[:result] = false }
86 | stub_action(VagrantPlugins::Proxmox::Action::CreateVm) { |env| env[:machine].id = 'node1/100' }
87 | stub_action(VagrantPlugins::Proxmox::Action::StartVm)
88 | stub_action(VagrantPlugins::Proxmox::Action::SyncFolders)
89 | execute_vagrant_command environment, :up, '--provider=proxmox'
90 | unstub_action(VagrantPlugins::Proxmox::Action::ConnectProxmox)
91 | unstub_action(VagrantPlugins::Proxmox::Action::GetNodeList)
92 | unstub_action(VagrantPlugins::Proxmox::Action::IsCreated)
93 | unstub_action(VagrantPlugins::Proxmox::Action::CreateVm)
94 | unstub_action(VagrantPlugins::Proxmox::Action::StartVm)
95 | unstub_action(VagrantPlugins::Proxmox::Action::SyncFolders)
96 | end
97 |
98 | def proxmox_api_url path
99 | "https://proxmox.example.com/api2/json/#{path}"
100 | end
101 |
102 | def add_dummy_box
103 | begin
104 | Vagrant::Environment.new.boxes.add 'dummy_box/dummy.box', 'b681e2bc-617b-4b35-94fa-edc92e1071b8', :proxmox
105 | rescue Vagrant::Errors::BoxAlreadyExists
106 | end
107 | end
108 |
109 | def remove_dummy_box
110 | execute_vagrant_command Vagrant::Environment.new, :box, 'remove', 'b681e2bc-617b-4b35-94fa-edc92e1071b8'
111 | end
112 |
--------------------------------------------------------------------------------
/spec/spec_helpers/time_helpers.rb:
--------------------------------------------------------------------------------
1 | require 'timecop'
2 |
3 | class Fixnum
4 |
5 | def self.time_interval_converter unit, factor
6 | define_method(unit) do
7 | TimeInterval.new self * factor, factor, unit
8 | end
9 | end
10 |
11 | time_interval_converter :second, 1
12 | time_interval_converter :seconds, 1
13 | time_interval_converter :minute, 60
14 | time_interval_converter :minutes, 60
15 | time_interval_converter :hour, 3600
16 | time_interval_converter :hours, 3600
17 | time_interval_converter :day, 86400
18 | time_interval_converter :days, 86400
19 |
20 | end
21 |
22 | class TimeInterval
23 |
24 | attr_reader :unit
25 | attr_reader :factor
26 |
27 | def initialize value, factor, unit
28 | @value = value.to_i
29 | @factor = factor
30 | @unit = unit
31 | end
32 |
33 | def clone_with_value value
34 | TimeInterval.new value, @factor, @unit
35 | end
36 |
37 | def inspect
38 | to_s
39 | end
40 |
41 | def to_s
42 | ("%.2f #{@unit}" % (in_units)).sub /\.00/, ''
43 | end
44 |
45 | def in_seconds
46 | @value
47 | end
48 |
49 | def in_units
50 | @value.to_f / @factor
51 | end
52 |
53 | def < other
54 | in_seconds < other.in_seconds
55 | end
56 |
57 | end
58 |
59 | class Timecop
60 |
61 | class << self
62 |
63 | alias :old_freeze :freeze
64 |
65 | def freeze *args, &block
66 | args << Time.local(5555) if args.empty?
67 | @frozen_time = args[0]
68 | old_freeze *args, &block
69 | end
70 |
71 | def frozen_time
72 | @frozen_time
73 | end
74 |
75 | def has_travelled? interval
76 | @frozen_time.to_i + interval.in_seconds == Time.now.to_i
77 | end
78 |
79 | end
80 | end
81 |
82 | RSpec::Matchers.define :have_elapsed do |expected|
83 | match do
84 | Timecop.has_travelled? expected
85 | end
86 | failure_message do
87 | result = expected.clone_with_value Time.now - Timecop.frozen_time
88 | "expected current time to be #{expected} later, but #{result < expected ? 'only' : 'already'} #{result} elapsed"
89 | end
90 | end
--------------------------------------------------------------------------------
/vagrant-proxmox.gemspec:
--------------------------------------------------------------------------------
1 | $:.unshift File.expand_path '../lib', __FILE__
2 | require 'vagrant-proxmox/version'
3 |
4 | Gem::Specification.new do |spec|
5 | spec.name = 'vagrant-proxmox'
6 | spec.version = VagrantPlugins::Proxmox::VERSION
7 | spec.platform = Gem::Platform::RUBY
8 | spec.license = 'MIT'
9 | spec.authors = ['Dirk Grappendorf', 'Tim Völpel', 'Sebastian Bremicker']
10 | spec.email = ['dirk.grappendorf@telcat.de', 'tim.voelpel@telcat.de', 'sebastian.bremicker@telcat.de']
11 | spec.homepage = 'https://github.com/telcat/vagrant-proxmox'
12 | spec.summary = 'Enables Vagrant to manage virtual machines on a Proxmox server.'
13 | spec.description = 'Enables Vagrant to manage virtual machines on a Proxmox server.'
14 | spec.required_ruby_version = '>= 2'
15 |
16 | spec.add_runtime_dependency 'rest-client', '~> 1.6.7'
17 | spec.add_runtime_dependency 'retryable', '~> 1.3.3'
18 | spec.add_runtime_dependency 'activesupport', '~> 4.0.0'
19 | spec.add_development_dependency 'rake', '10.5.0'
20 | spec.add_development_dependency 'rspec', '~> 3.0.0'
21 | spec.add_development_dependency 'simplecov', '~> 0.9.0'
22 | spec.add_development_dependency 'simplecov-rcov', '~> 0.2.3'
23 | spec.add_development_dependency 'geminabox', '~> 0.11.1'
24 | spec.add_development_dependency 'guard-rspec', '4.2.10'
25 | spec.add_development_dependency 'libnotify', '~> 0.8.3'
26 | spec.add_development_dependency 'timecop', '~>0.7.1'
27 | spec.add_development_dependency 'cucumber', '~>1.3.15'
28 | spec.add_development_dependency 'webmock', '~> 1.18.0'
29 | spec.add_development_dependency 'awesome_print', '~> 1.2.0'
30 |
31 | spec.files = Dir.glob('lib/**/*.rb') + Dir.glob('locales/**/*.yml')
32 | spec.test_files = Dir.glob 'spec/**/*.rb'
33 | spec.require_paths = %w(lib)
34 | end
35 |
--------------------------------------------------------------------------------
/vagrant-proxmox.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------