├── .github └── workflows │ └── build.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── Vagrantfile ├── dev ├── provisioning │ ├── devstack.sh │ ├── main.sh │ └── vagrant-save.sh └── test │ ├── .gitignore │ └── Vagrantfile ├── samples ├── .gitignore ├── 01_simple.001.bats ├── 01_simple.002.bats ├── 01_simple.003.bats ├── 01_simple.004.bats ├── 01_simple.005.bats ├── 01_simple.006.bats ├── 01_simple │ └── Vagrantfile ├── 02_multimachine.001.bats ├── 02_multimachine │ └── Vagrantfile ├── 03_multimachine_loop.001.bats ├── 03_multimachine_loop.002.bats ├── 03_multimachine_loop.003.bats ├── 03_multimachine_loop │ └── Vagrantfile ├── 04_heat_stack.001.bats ├── 04_heat_stack │ ├── Vagrantfile │ └── stack.yml ├── 05_heat_stack_multimachine.001.bats ├── 05_heat_stack_multimachine │ ├── Vagrantfile │ └── stack.yml ├── 06_keystone_v3.001.bats ├── 06_keystone_v3 │ └── Vagrantfile ├── 07_insert_key_false.001.bats ├── 07_insert_key_false │ ├── Vagrantfile │ └── ssh_key ├── 09_with_http_proxy.001.bats ├── 09_with_http_proxy │ └── Vagrantfile ├── README.md ├── openrc-common.sh ├── openrc-keystone-v2.sh ├── openrc-keystone-v3.sh └── test_helper.bash └── source ├── .gitignore ├── .rubocop.yml ├── CHANGELOG.md ├── Gemfile ├── RELEASE.md ├── Rakefile ├── Vagrantfile ├── dummy.box ├── example_box ├── README.md └── metadata.json ├── functional_tests ├── Vagrantfile ├── keys │ ├── vagrant-openstack │ └── vagrant-openstack.pub └── run_tests.sh ├── lib ├── vagrant-openstack-provider.rb └── vagrant-openstack-provider │ ├── action.rb │ ├── action │ ├── abstract_action.rb │ ├── connect_openstack.rb │ ├── create_server.rb │ ├── create_stack.rb │ ├── delete_server.rb │ ├── delete_stack.rb │ ├── message.rb │ ├── provision.rb │ ├── read_ssh_info.rb │ ├── read_state.rb │ ├── resume.rb │ ├── snapshot_cleanup.rb │ ├── snapshot_delete.rb │ ├── snapshot_list.rb │ ├── snapshot_restore.rb │ ├── snapshot_save.rb │ ├── start_server.rb │ ├── stop_server.rb │ ├── suspend.rb │ ├── sync_folders.rb │ ├── wait_active.rb │ └── wait_stop.rb │ ├── cap │ └── snapshot_list.rb │ ├── catalog │ └── openstack_catalog.rb │ ├── client │ ├── cinder.rb │ ├── domain.rb │ ├── glance.rb │ ├── heat.rb │ ├── http_utils.rb │ ├── keystone.rb │ ├── neutron.rb │ ├── nova.rb │ ├── openstack.rb │ ├── request_logger.rb │ └── rest_utils.rb │ ├── command │ ├── abstract_command.rb │ ├── flavor_list.rb │ ├── floatingip_list.rb │ ├── image_list.rb │ ├── main.rb │ ├── network_list.rb │ ├── openstack_command.rb │ ├── reset.rb │ ├── subnet_list.rb │ ├── utils.rb │ └── volume_list.rb │ ├── config.rb │ ├── config │ └── http.rb │ ├── config_resolver.rb │ ├── errors.rb │ ├── logging.rb │ ├── plugin.rb │ ├── provider.rb │ ├── utils.rb │ ├── version.rb │ └── version_checker.rb ├── locales └── en.yml ├── spec └── vagrant-openstack-provider │ ├── action │ ├── connect_openstack_spec.rb │ ├── create_server_spec.rb │ ├── create_stack_spec.rb │ ├── delete_server_spec.rb │ ├── delete_stack_spec.rb │ ├── message_spec.rb │ ├── provision_spec.rb │ ├── read_ssh_info_spec.rb │ ├── read_state_spec.rb │ ├── resume_server_spec.rb │ ├── start_server_spec.rb │ ├── stop_server_spec.rb │ ├── suspend_server_spec.rb │ ├── sync_folders_spec.rb │ ├── wait_active_spec.rb │ └── wait_stop_spec.rb │ ├── action_spec.rb │ ├── client │ ├── cinder_spec.rb │ ├── glance_spec.rb │ ├── heat_spec.rb │ ├── keystone_spec.rb │ ├── neutron_spec.rb │ ├── nova_spec.rb │ └── utils_spec.rb │ ├── command │ ├── flavor_list_spec.rb │ ├── floatingip_list_spec.rb │ ├── image_list_spec.rb │ ├── network_list_spec.rb │ ├── reset_spec.rb │ ├── subnet_list_spec.rb │ └── volume_list_spec.rb │ ├── config_resolver_spec.rb │ ├── config_spec.rb │ ├── e2e_spec.rb.save │ ├── provider_spec.rb │ ├── spec_helper.rb │ ├── utils_spec.rb │ └── version_checker_spec.rb ├── stackrc └── vagrant-openstack-provider.gemspec /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: 6 | contents: read 7 | 8 | jobs: 9 | 10 | build: 11 | runs-on: ubuntu-20.04 12 | 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - name: Install bundler 17 | run: | 18 | sudo gem install bundler 19 | 20 | - name: Install dependencies 21 | run: | 22 | cd source 23 | bundle install 24 | 25 | - name: Run tests 26 | run: | 27 | cd source 28 | bundle exec rake 29 | 30 | - name: Build gem package 31 | run: | 32 | cd source 33 | gem build *.gemspec 34 | mv $(ls -1 | grep -e "\.gem$") vagrant-openstack-provider.gem 35 | 36 | - name: Archive gem package 37 | uses: actions/upload-artifact@v3 38 | with: 39 | name: vagrant-openstack-provider.gem 40 | path: source/vagrant-openstack-provider.gem 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.iml 3 | *.rbc 4 | .bundle 5 | .config 6 | .vagrant 7 | .yardoc 8 | Gemfile.lock 9 | gemfiles/*.lock 10 | InstalledFiles 11 | _yardoc 12 | coverage 13 | doc/ 14 | lib/bundler/man 15 | pkg 16 | rdoc 17 | spec/reports 18 | test/tmp 19 | test/version_tmp 20 | tmp 21 | .venv 22 | .idea/ 23 | *.swp 24 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | We like to encourage you to contribute to the repository. 4 | This should be as easy as possible for you but there are a few things to consider when contributing. 5 | The following guidelines for contribution should be followed if you want to submit a pull request. 6 | 7 | ## How to prepare 8 | 9 | * You need a [GitHub account](https://github.com/signup/free) 10 | * Submit an [issue ticket](https://github.com/ggiamarchi/vagrant-openstack-provider/issues) for your issue if there is none yet. 11 | * Describe the issue and include steps to reproduce when it's a bug. 12 | * Ensure to mention the earliest version that you know is affected, as well as the vagrant version you are using 13 | * If you plan on submitting a bug report, please submit debug-level logs along 14 | with the report using [gist](https://gist.github.com/) or some other paste 15 | service by prepending `VAGRANT_OPENSTACK_LOG=debug` to your `vagrant` commands. 16 | * Fork the repository on GitHub 17 | 18 | ## Make Changes 19 | 20 | * In your forked repository, create a topic branch for your upcoming patch. 21 | * Usually this is based on the master branch. 22 | * Create a branch based on master; `git branch 23 | fix/master/my_contribution master` then checkout the new branch with `git 24 | checkout fix/master/my_contribution`. Please avoid working directly on the `master` branch. 25 | * Make commits of logical units and describe them properly. 26 | * Check for unnecessary whitespace with `git diff --check` before committing. 27 | 28 | * If possible, submit tests to your patch / new feature so it can be tested easily. 29 | * Assure nothing is broken by running all the tests. 30 | 31 | ## Submit Changes 32 | 33 | * Push your changes to a topic branch in your fork of the repository. 34 | * Open a pull request to the original repository and choose the right original branch you want to patch. 35 | * If not done in commit messages (which you really should do) please reference and update your issue with the code changes. 36 | * Even if you have write access to the repository, do not directly push or merge pull-requests. Let another team member review your pull request and approve. 37 | 38 | # Additional Resources 39 | 40 | * [General GitHub documentation](http://help.github.com/) 41 | * [GitHub pull request documentation](http://help.github.com/send-pull-requests/) 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Mitchell Hashimoto 2 | Copyright (c) 2015 Guillaume Giamarchi, Julien Vey 3 | 4 | MIT License 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | Vagrant.configure('2') do |config| 5 | 6 | config.vm.provider 'virtualbox' do |vb| 7 | vb.memory = 8192 8 | end 9 | 10 | config.vm.box = "ubuntu/focal64" 11 | 12 | config.vm.network "forwarded_port", guest: 80, host: 8080 13 | 14 | config.vm.provision "shell", path: "dev/provisioning/main.sh", privileged: false 15 | end 16 | -------------------------------------------------------------------------------- /dev/provisioning/devstack.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | cd $HOME 6 | 7 | ### Prepare host 8 | 9 | sudo apt install -y net-tools 10 | 11 | ### Get Devstack sources 12 | 13 | git clone https://opendev.org/openstack/devstack 14 | cd devstack 15 | 16 | ### Create devstack configuration 17 | 18 | cat > local.conf <<'EOF' 19 | [[local|localrc]] 20 | ADMIN_PASSWORD=secret 21 | DATABASE_PASSWORD=$ADMIN_PASSWORD 22 | RABBIT_PASSWORD=$ADMIN_PASSWORD 23 | SERVICE_PASSWORD=$ADMIN_PASSWORD 24 | EOF 25 | 26 | HOST_IP=$(ip route | grep -e "^default" | sed -e "s/.* src \([^ ]*\) .*/\1/") 27 | 28 | cat >> local.conf <> .bashrc <<'EOF' 23 | 24 | vagrant() { 25 | RUN_DIR=$PWD 26 | ( 27 | cd /vagrant/source 28 | VAGRANT_OPENSTACK_LOG=debug VAGRANT_CWD=${RUN_DIR} bundle exec vagrant $* 29 | ) 30 | } 31 | EOF 32 | 33 | ### Create stack user for Devstack 34 | 35 | sudo useradd -s /bin/bash -d /opt/stack -m stack 36 | sudo chmod +x /opt/stack 37 | echo "stack ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/stack 38 | 39 | ### Install devstack in background 40 | 41 | sudo -u stack -s -- <<'EOF' 42 | nohup /vagrant/dev/provisioning/devstack.sh > $HOME/devstack.log 2>&1 & 43 | EOF 44 | 45 | set +x 46 | 47 | echo "" 48 | echo "" 49 | echo " !!! Devstack installation process is running in backgroud !!!" 50 | echo " !!! It can take a while, mainly depending on your internet bandwidth !!!" 51 | echo " !!! !!!" 52 | echo " !!! See progress in /opt/stack/devstack.log !!!" 53 | echo "" 54 | echo "" 55 | -------------------------------------------------------------------------------- /dev/provisioning/vagrant-save.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor | sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg 6 | echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list 7 | sudo apt update 8 | sudo apt install -y vagrant 9 | 10 | vagrant plugin install vagrant-openstack-provider 11 | 12 | mkdir $HOME/vagrant 13 | cd $HOME/vagrant 14 | 15 | cat > Vagrantfile <<'EOF' 16 | # -*- mode: ruby -*- 17 | # vi: set ft=ruby : 18 | 19 | Vagrant.configure('2') do |config| 20 | 21 | config.ssh.username = 'ubuntu' 22 | config.vm.boot_timeout = 500 23 | 24 | config.vm.provider :openstack do |os, ov| 25 | os.server_name = 'vagrant' 26 | 27 | os.identity_api_version = '3' 28 | os.domain_name = ENV['OS_PROJECT_DOMAIN_ID'] 29 | os.project_name = ENV['OS_PROJECT_NAME'] 30 | os.openstack_auth_url = ENV['OS_AUTH_URL'] + '/v3' 31 | os.username = ENV['OS_USERNAME'] 32 | os.password = ENV['OS_PASSWORD'] 33 | os.region = ENV['OS_REGION_NAME'] 34 | os.floating_ip_pool = 'public' 35 | os.floating_ip_pool_always_allocate = true 36 | os.flavor = 'ds512M' 37 | os.image = 'ubuntu-18.04' 38 | os.networks = ['private'] 39 | os.security_groups = ['default'] 40 | 41 | os.openstack_network_url = 'http://10.0.2.15:9696/networking/v2.0' 42 | 43 | ov.nfs.functional = false 44 | end 45 | end 46 | EOF 47 | 48 | source $HOME/devstack/openrc 49 | VAGRANT_OPENSTACK_LOG=debug vagrant up 50 | -------------------------------------------------------------------------------- /dev/test/.gitignore: -------------------------------------------------------------------------------- 1 | .vagrant/ 2 | -------------------------------------------------------------------------------- /dev/test/Vagrantfile: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider' 2 | 3 | Vagrant.configure('2') do |config| 4 | 5 | config.ssh.username = 'ubuntu' 6 | config.vm.boot_timeout = 500 7 | 8 | config.vm.provider :openstack do |os, ov| 9 | os.server_name = 'vagrant' 10 | 11 | os.identity_api_version = '3' 12 | os.domain_name = 'default' 13 | os.project_name = 'demo' 14 | os.openstack_auth_url = 'http://10.0.2.15/identity/v3' 15 | os.username = 'demo' 16 | os.password = 'secret' 17 | os.region = 'RegionOne' 18 | os.floating_ip_pool = 'public' 19 | os.floating_ip_pool_always_allocate = true 20 | os.flavor = 'ds512M' 21 | os.image = 'ubuntu-18.04' 22 | os.networks = ['private'] 23 | os.security_groups = ['default'] 24 | 25 | ov.nfs.functional = false 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /samples/.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | **/.vagrant 3 | -------------------------------------------------------------------------------- /samples/01_simple.001.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load test_helper 4 | 5 | @test "01 - Simple - without any floating IP pre-allocated / using floating IP pool name / don't force allocate" { 6 | title "$BATS_TEST_DESCRIPTION" 7 | 8 | export VAGRANT_CWD=$BATS_TEST_DIRNAME/01_simple 9 | export OS_FLOATING_IP_POOL=${OS_FLOATING_IP_POOL_NAME} 10 | 11 | [ $(openstack floating ip list -f value | wc -l) -eq 0 ] # Check no IPs is allocated 12 | 13 | run exec_vagrant up 14 | flush_out 15 | [ "$status" -eq 0 ] 16 | [ $(openstack floating ip list -f value | wc -l) -eq 1 ] # Check one IP is allocated 17 | 18 | run exec_vagrant ssh -c "true" 19 | flush_out 20 | [ "$status" -eq 0 ] 21 | 22 | run exec_vagrant destroy 23 | flush_out 24 | [ "$status" -eq 0 ] 25 | } 26 | -------------------------------------------------------------------------------- /samples/01_simple.002.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load test_helper 4 | 5 | @test "01 - Simple - without any floating IP pre-allocated / using floating IP pool ID / don't force allocate" { 6 | title "$BATS_TEST_DESCRIPTION" 7 | 8 | export VAGRANT_CWD=$BATS_TEST_DIRNAME/01_simple 9 | export OS_FLOATING_IP_POOL=${OS_FLOATING_IP_POOL_ID} 10 | 11 | [ $(openstack floating ip list -f value | wc -l) -eq 0 ] # Check no IPs is allocated 12 | 13 | run exec_vagrant up 14 | flush_out 15 | [ "$status" -eq 0 ] 16 | [ $(openstack floating ip list -f value | wc -l) -eq 1 ] # Check one IP is allocated 17 | 18 | run exec_vagrant ssh -c "true" 19 | flush_out 20 | [ "$status" -eq 0 ] 21 | 22 | run exec_vagrant destroy 23 | flush_out 24 | [ "$status" -eq 0 ] 25 | } 26 | -------------------------------------------------------------------------------- /samples/01_simple.003.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load test_helper 4 | 5 | @test "01 - Simple - with floating IPs pre-allocated / using floating IP pool name / don't force allocate" { 6 | title "$BATS_TEST_DESCRIPTION" 7 | 8 | allocate_4_floating_ip 9 | [ $(openstack floating ip list -f value | wc -l) -eq 4 ] # Check 4 IPs are allocated 10 | 11 | export VAGRANT_CWD=$BATS_TEST_DIRNAME/01_simple 12 | export OS_FLOATING_IP_ALWAYS_ALLOCATE=false 13 | export OS_FLOATING_IP_POOL=${OS_FLOATING_IP_POOL_NAME} 14 | 15 | run exec_vagrant up 16 | flush_out 17 | [ "$status" -eq 0 ] 18 | [ $(openstack floating ip list -f value | wc -l) -eq 4 ] # Check again 4 IPs are allocated 19 | 20 | run exec_vagrant ssh -c "true" 21 | flush_out 22 | [ "$status" -eq 0 ] 23 | 24 | run exec_vagrant destroy 25 | flush_out 26 | [ "$status" -eq 0 ] 27 | } 28 | -------------------------------------------------------------------------------- /samples/01_simple.004.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load test_helper 4 | 5 | @test "01 - Simple - with floating IPs pre-allocated / using floating IP pool name / force allocate" { 6 | title "$BATS_TEST_DESCRIPTION" 7 | 8 | allocate_4_floating_ip 9 | [ $(openstack floating ip list -f value | wc -l) -eq 4 ] # Check 4 IPs are allocated 10 | 11 | export VAGRANT_CWD=$BATS_TEST_DIRNAME/01_simple 12 | export OS_FLOATING_IP_ALWAYS_ALLOCATE=true 13 | export OS_FLOATING_IP_POOL=${OS_FLOATING_IP_POOL_NAME} 14 | 15 | run exec_vagrant up 16 | flush_out 17 | [ "$status" -eq 0 ] 18 | [ $(openstack floating ip list -f value | wc -l) -eq 5 ] # Check again 4 IPs are allocated 19 | 20 | run exec_vagrant ssh -c "true" 21 | flush_out 22 | [ "$status" -eq 0 ] 23 | 24 | run exec_vagrant destroy 25 | flush_out 26 | [ "$status" -eq 0 ] 27 | } 28 | -------------------------------------------------------------------------------- /samples/01_simple.005.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load test_helper 4 | 5 | # TODO This test fails because of issue #325 6 | @test "01 - Simple - with floating IPs pre-allocated / using floating IP pool ID / don't force allocate" { 7 | title "$BATS_TEST_DESCRIPTION" 8 | skip "This test fails because of issue #325" 9 | 10 | allocate_4_floating_ip 11 | [ $(openstack floating ip list -f value | wc -l) -eq 4 ] # Check 4 IPs are allocated 12 | 13 | export VAGRANT_CWD=$BATS_TEST_DIRNAME/01_simple 14 | export OS_FLOATING_IP_ALWAYS_ALLOCATE=false 15 | export OS_FLOATING_IP_POOL=${OS_FLOATING_IP_POOL_ID} 16 | 17 | run exec_vagrant up 18 | flush_out 19 | [ "$status" -eq 0 ] 20 | [ $(openstack floating ip list -f value | wc -l) -eq 4 ] # Check again 4 IPs are allocated 21 | 22 | run exec_vagrant ssh -c "true" 23 | flush_out 24 | [ "$status" -eq 0 ] 25 | 26 | run exec_vagrant destroy 27 | flush_out 28 | [ "$status" -eq 0 ] 29 | } 30 | -------------------------------------------------------------------------------- /samples/01_simple.006.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load test_helper 4 | 5 | @test "01 - Simple - with floating IPs pre-allocated / using floating IP pool ID / force allocate" { 6 | title "$BATS_TEST_DESCRIPTION" 7 | 8 | allocate_4_floating_ip 9 | [ $(openstack floating ip list -f value | wc -l) -eq 4 ] # Check 4 IPs are allocated 10 | 11 | export VAGRANT_CWD=$BATS_TEST_DIRNAME/01_simple 12 | export OS_FLOATING_IP_ALWAYS_ALLOCATE=true 13 | export OS_FLOATING_IP_POOL=${OS_FLOATING_IP_POOL_ID} 14 | 15 | run exec_vagrant up 16 | flush_out 17 | [ "$status" -eq 0 ] 18 | [ $(openstack floating ip list -f value | wc -l) -eq 5 ] # Check again 4 IPs are allocated 19 | 20 | run exec_vagrant ssh -c "true" 21 | flush_out 22 | [ "$status" -eq 0 ] 23 | 24 | run exec_vagrant destroy 25 | flush_out 26 | [ "$status" -eq 0 ] 27 | } 28 | -------------------------------------------------------------------------------- /samples/01_simple/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # 5 | # This is quite the minimal configuration necessary 6 | # to start an OpenStack instance using Vagrant. 7 | # 8 | # This example assumes a floating IP is needed to 9 | # reach the machine, although you might remove the 10 | # floating_ip_pool parameter if you are able to join 11 | # the instance using its private IP address (e.g. 12 | # through a VPN). 13 | # 14 | Vagrant.configure('2') do |config| 15 | 16 | config.ssh.username = ENV['OS_SSH_USERNAME'] 17 | 18 | config.vm.provider :openstack do |os, ov| 19 | os.server_name = '01_simple' 20 | os.openstack_auth_url = ENV['OS_AUTH_URL'] 21 | os.tenant_name = ENV['OS_TENANT_NAME'] 22 | os.username = ENV['OS_USERNAME'] 23 | os.password = ENV['OS_PASSWORD'] 24 | os.region = ENV['OS_REGION_NAME'] 25 | os.floating_ip_pool = ENV['OS_FLOATING_IP_POOL'] 26 | os.floating_ip_pool_always_allocate = (ENV['OS_FLOATING_IP_ALWAYS_ALLOCATE'] == 'true') 27 | os.flavor = ENV['OS_FLAVOR'] 28 | os.image = ENV['OS_IMAGE'] 29 | os.networks << ENV['OS_NETWORK'] 30 | 31 | ov.nfs.functional = false 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /samples/02_multimachine.001.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load test_helper 4 | 5 | @test "02 - Multimachine" { 6 | title "$BATS_TEST_DESCRIPTION" 7 | 8 | export VAGRANT_CWD=$BATS_TEST_DIRNAME/02_multimachine 9 | 10 | run exec_vagrant up 11 | flush_out 12 | [ "$status" -eq 0 ] 13 | 14 | run exec_vagrant ssh -c "true" server-1 15 | flush_out 16 | [ "$status" -eq 0 ] 17 | 18 | run exec_vagrant ssh -c "true" server-2 19 | flush_out 20 | [ "$status" -eq 0 ] 21 | 22 | run exec_vagrant destroy 23 | flush_out 24 | [ "$status" -eq 0 ] 25 | } 26 | -------------------------------------------------------------------------------- /samples/02_multimachine/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # 5 | # Basic multi-machine example 6 | # 7 | Vagrant.configure('2') do |config| 8 | 9 | config.vm.provider :openstack do |os, ov| 10 | os.openstack_auth_url = ENV['OS_AUTH_URL'] 11 | os.tenant_name = ENV['OS_TENANT_NAME'] 12 | os.username = ENV['OS_USERNAME'] 13 | os.password = ENV['OS_PASSWORD'] 14 | os.floating_ip_pool = ENV['OS_FLOATING_IP_POOL'] 15 | os.flavor = ENV['OS_FLAVOR'] 16 | os.image = ENV['OS_IMAGE'] 17 | os.networks << ENV['OS_NETWORK'] 18 | 19 | ov.nfs.functional = false 20 | end 21 | 22 | config.vm.define 'server-1' do |s| 23 | s.vm.provider :openstack do |os, override| 24 | os.server_name = '02_multimachine-1' 25 | override.ssh.username = ENV['OS_SSH_USERNAME'] 26 | end 27 | end 28 | 29 | config.vm.define 'server-2' do |s| 30 | s.vm.provider :openstack do |os, override| 31 | os.server_name = '02_multimachine-2' 32 | override.ssh.username = ENV['OS_SSH_USERNAME'] 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /samples/03_multimachine_loop.001.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load test_helper 4 | 5 | @test "03 - Multimachine loop" { 6 | title "$BATS_TEST_DESCRIPTION" 7 | 8 | export VAGRANT_CWD=$BATS_TEST_DIRNAME/03_multimachine_loop 9 | 10 | run exec_vagrant up 11 | flush_out 12 | [ "$status" -eq 0 ] 13 | 14 | run exec_vagrant ssh -c "true" server-1 15 | flush_out 16 | [ "$status" -eq 0 ] 17 | 18 | run exec_vagrant ssh -c "true" server-2 19 | flush_out 20 | [ "$status" -eq 0 ] 21 | 22 | run exec_vagrant ssh -c "true" server-3 23 | flush_out 24 | [ "$status" -eq 0 ] 25 | 26 | run exec_vagrant destroy 27 | flush_out 28 | [ "$status" -eq 0 ] 29 | } 30 | -------------------------------------------------------------------------------- /samples/03_multimachine_loop.002.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load test_helper 4 | 5 | @test "03 - Multimachine loop / in parallel" { 6 | title "$BATS_TEST_DESCRIPTION" 7 | 8 | export VAGRANT_CWD=$BATS_TEST_DIRNAME/03_multimachine_loop 9 | 10 | run exec_vagrant up --parallel 11 | flush_out 12 | [ "$status" -eq 0 ] 13 | 14 | run exec_vagrant ssh -c "true" server-1 15 | flush_out 16 | [ "$status" -eq 0 ] 17 | 18 | run exec_vagrant ssh -c "true" server-2 19 | flush_out 20 | [ "$status" -eq 0 ] 21 | 22 | run exec_vagrant ssh -c "true" server-3 23 | flush_out 24 | [ "$status" -eq 0 ] 25 | 26 | run exec_vagrant ssh -c "true" server-4 27 | flush_out 28 | [ "$status" -eq 0 ] 29 | 30 | run exec_vagrant ssh -c "true" server-5 31 | flush_out 32 | [ "$status" -eq 0 ] 33 | 34 | run exec_vagrant ssh -c "true" server-6 35 | flush_out 36 | [ "$status" -eq 0 ] 37 | 38 | run exec_vagrant destroy 39 | flush_out 40 | [ "$status" -eq 0 ] 41 | } 42 | -------------------------------------------------------------------------------- /samples/03_multimachine_loop.003.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load test_helper 4 | 5 | @test "03 - Multimachine loop / in parallel / with pre allocated floating IP" { 6 | title "$BATS_TEST_DESCRIPTION" 7 | 8 | allocate_4_floating_ip 9 | export VAGRANT_CWD=$BATS_TEST_DIRNAME/03_multimachine_loop 10 | 11 | run exec_vagrant up --parallel 12 | flush_out 13 | [ "$status" -eq 0 ] 14 | 15 | run exec_vagrant ssh -c "true" server-1 16 | flush_out 17 | [ "$status" -eq 0 ] 18 | 19 | run exec_vagrant ssh -c "true" server-2 20 | flush_out 21 | [ "$status" -eq 0 ] 22 | 23 | run exec_vagrant ssh -c "true" server-3 24 | flush_out 25 | [ "$status" -eq 0 ] 26 | 27 | run exec_vagrant ssh -c "true" server-4 28 | flush_out 29 | [ "$status" -eq 0 ] 30 | 31 | run exec_vagrant ssh -c "true" server-5 32 | flush_out 33 | [ "$status" -eq 0 ] 34 | 35 | run exec_vagrant ssh -c "true" server-6 36 | flush_out 37 | [ "$status" -eq 0 ] 38 | 39 | run exec_vagrant destroy 40 | flush_out 41 | [ "$status" -eq 0 ] 42 | } 43 | -------------------------------------------------------------------------------- /samples/03_multimachine_loop/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # 5 | # Programmatic loop multi-machine example 6 | # 7 | Vagrant.configure('2') do |config| 8 | 9 | config.vm.provider :openstack do |os, ov| 10 | os.openstack_auth_url = ENV['OS_AUTH_URL'] 11 | os.tenant_name = ENV['OS_TENANT_NAME'] 12 | os.username = ENV['OS_USERNAME'] 13 | os.password = ENV['OS_PASSWORD'] 14 | os.floating_ip_pool = ENV['OS_FLOATING_IP_POOL'] 15 | os.flavor = ENV['OS_FLAVOR'] 16 | os.image = ENV['OS_IMAGE'] 17 | os.networks << ENV['OS_NETWORK'] 18 | 19 | ov.nfs.functional = false 20 | end 21 | 22 | (1..6).each do |i| 23 | config.vm.define "server-#{i}" do |s| 24 | s.vm.provider :openstack do |os, override| 25 | os.server_name = "03_multimachine_loop-#{i}" 26 | override.ssh.username = ENV['OS_SSH_USERNAME'] 27 | end 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /samples/04_heat_stack.001.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load test_helper 4 | 5 | @test "04 - Heat Stack" { 6 | title "$BATS_TEST_DESCRIPTION" 7 | 8 | export VAGRANT_CWD=$BATS_TEST_DIRNAME/04_heat_stack 9 | 10 | run exec_vagrant up 11 | flush_out 12 | [ "$status" -eq 0 ] 13 | 14 | run exec_vagrant ssh -c "true" server-1 15 | flush_out 16 | [ "$status" -eq 0 ] 17 | 18 | run exec_vagrant destroy 19 | flush_out 20 | [ "$status" -eq 0 ] 21 | } 22 | -------------------------------------------------------------------------------- /samples/04_heat_stack/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | Vagrant.configure('2') do |config| 5 | 6 | config.vm.provider :openstack do |os, ov| 7 | os.openstack_auth_url = ENV['OS_AUTH_URL'] 8 | os.tenant_name = ENV['OS_TENANT_NAME'] 9 | os.username = ENV['OS_USERNAME'] 10 | os.password = ENV['OS_PASSWORD'] 11 | os.floating_ip_pool = ENV['OS_FLOATING_IP_POOL'] 12 | os.flavor = ENV['OS_FLAVOR'] 13 | os.image = ENV['OS_IMAGE'] 14 | os.security_groups = ['sg'] 15 | os.networks << 'heat-net' 16 | os.stacks << {name: 'vagrant', template: "#{File.dirname(__FILE__)}/stack.yml"} 17 | 18 | ov.nfs.functional = false 19 | end 20 | 21 | config.vm.define 'server-1' do |s| 22 | s.vm.provider :openstack do |os, override| 23 | os.server_name = '04_Heat_Stack-1' 24 | override.ssh.username = ENV['OS_SSH_USERNAME'] 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /samples/04_heat_stack/stack.yml: -------------------------------------------------------------------------------- 1 | heat_template_version: 2013-05-23 2 | 3 | description: > 4 | Create a minimal infrastructure to spawn an instance 5 | 6 | parameters: 7 | 8 | external_network_id: 9 | type: string 10 | label: External network id 11 | description: External network to connect the router 12 | default: 5cccfde8-ddae-4feb-9a97-ca6532d1577f 13 | 14 | resources: 15 | 16 | security_group: 17 | type: OS::Neutron::SecurityGroup 18 | properties: 19 | name: sg 20 | description: Add security group rules for server 21 | rules: 22 | - remote_ip_prefix: 0.0.0.0/0 23 | direction: ingress 24 | - remote_ip_prefix: 0.0.0.0/0 25 | direction: egress 26 | 27 | network: 28 | type: OS::Neutron::Net 29 | properties: 30 | name: heat-net 31 | subnet: 32 | type: OS::Neutron::Subnet 33 | properties: 34 | network_id: { get_resource: network } 35 | cidr: 10.0.0.0/24 36 | allocation_pools: 37 | - { start: 10.0.0.100, end: 10.0.0.250 } 38 | 39 | router: 40 | type: OS::Neutron::Router 41 | properties: 42 | name: router 43 | router-gateway: 44 | type: OS::Neutron::RouterGateway 45 | properties: 46 | router_id: { get_resource: router } 47 | network_id: { get_param: external_network_id } 48 | router-dataplane-int: 49 | type: OS::Neutron::RouterInterface 50 | properties: 51 | router_id: { get_resource: router } 52 | subnet_id: { get_resource: subnet } 53 | -------------------------------------------------------------------------------- /samples/05_heat_stack_multimachine.001.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load test_helper 4 | 5 | @test "05 - Heat Stack multimachine" { 6 | skip "Does not work currently" 7 | title "$BATS_TEST_DESCRIPTION" 8 | 9 | export VAGRANT_CWD=$BATS_TEST_DIRNAME/05_heat_stack_multimachine 10 | 11 | run exec_vagrant up 12 | flush_out 13 | [ "$status" -eq 0 ] 14 | 15 | run exec_vagrant ssh -c "true" server-1 16 | flush_out 17 | [ "$status" -eq 0 ] 18 | 19 | run exec_vagrant ssh -c "true" server-2 20 | flush_out 21 | [ "$status" -eq 0 ] 22 | 23 | run exec_vagrant destroy 24 | flush_out 25 | [ "$status" -eq 0 ] 26 | } 27 | -------------------------------------------------------------------------------- /samples/05_heat_stack_multimachine/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | Vagrant.configure('2') do |config| 5 | 6 | config.vm.provider :openstack do |os, ov| 7 | os.openstack_auth_url = ENV['OS_AUTH_URL'] 8 | os.tenant_name = ENV['OS_TENANT_NAME'] 9 | os.username = ENV['OS_USERNAME'] 10 | os.password = ENV['OS_PASSWORD'] 11 | os.floating_ip_pool = ENV['OS_FLOATING_IP_POOL'] 12 | os.flavor = ENV['OS_FLAVOR'] 13 | os.image = ENV['OS_IMAGE'] 14 | os.security_groups = ['sg'] 15 | os.networks << 'heat-net' 16 | os.stacks << {name: 'vagrant', template: "#{File.dirname(__FILE__)}/stack.yml"} 17 | 18 | ov.nfs.functional = false 19 | end 20 | 21 | config.vm.define 'server-1' do |s| 22 | s.vm.provider :openstack do |os, override| 23 | os.server_name = '04_Heat_Stack-1' 24 | override.ssh.username = ENV['OS_SSH_USERNAME'] 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /samples/05_heat_stack_multimachine/stack.yml: -------------------------------------------------------------------------------- 1 | heat_template_version: 2013-05-23 2 | 3 | description: > 4 | Create a minimal infrastructure to spawn an instance 5 | 6 | parameters: 7 | 8 | external_network_id: 9 | type: string 10 | label: External network id 11 | description: External network to connect the router 12 | default: 5cccfde8-ddae-4feb-9a97-ca6532d1577f 13 | 14 | resources: 15 | 16 | security_group: 17 | type: OS::Neutron::SecurityGroup 18 | properties: 19 | name: sg 20 | description: Add security group rules for server 21 | rules: 22 | - remote_ip_prefix: 0.0.0.0/0 23 | direction: ingress 24 | - remote_ip_prefix: 0.0.0.0/0 25 | direction: egress 26 | 27 | network: 28 | type: OS::Neutron::Net 29 | properties: 30 | name: heat-net 31 | subnet: 32 | type: OS::Neutron::Subnet 33 | properties: 34 | network_id: { get_resource: network } 35 | cidr: 10.0.0.0/24 36 | allocation_pools: 37 | - { start: 10.0.0.100, end: 10.0.0.250 } 38 | 39 | router: 40 | type: OS::Neutron::Router 41 | properties: 42 | name: router 43 | router-gateway: 44 | type: OS::Neutron::RouterGateway 45 | properties: 46 | router_id: { get_resource: router } 47 | network_id: { get_param: external_network_id } 48 | router-dataplane-int: 49 | type: OS::Neutron::RouterInterface 50 | properties: 51 | router_id: { get_resource: router } 52 | subnet_id: { get_resource: subnet } 53 | -------------------------------------------------------------------------------- /samples/06_keystone_v3.001.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load test_helper 4 | 5 | @test "06 - Keystone v3" { 6 | title "$BATS_TEST_DESCRIPTION" 7 | 8 | unset_openstack_env 9 | source $BATS_TEST_DIRNAME/openrc-keystone-v3.sh 10 | 11 | export VAGRANT_CWD=$BATS_TEST_DIRNAME/06_keystone_v3 12 | 13 | run exec_vagrant up 14 | flush_out 15 | [ "$status" -eq 0 ] 16 | 17 | run exec_vagrant ssh -c "true" 18 | flush_out 19 | [ "$status" -eq 0 ] 20 | 21 | run exec_vagrant destroy 22 | flush_out 23 | [ "$status" -eq 0 ] 24 | } 25 | 26 | -------------------------------------------------------------------------------- /samples/06_keystone_v3/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # 5 | # This is quite the minimal configuration necessary 6 | # to start an OpenStack instance using Vagrant on 7 | # an OpenStack with Keystone v3 API 8 | # 9 | Vagrant.configure('2') do |config| 10 | 11 | config.ssh.username = ENV['OS_SSH_USERNAME'] 12 | 13 | config.vm.provider :openstack do |os, ov| 14 | os.server_name = '05_keystone_v3' 15 | os.identity_api_version = '3' 16 | os.openstack_auth_url = ENV['OS_AUTH_URL'] 17 | os.project_name = ENV['OS_PROJECT_NAME'] 18 | os.user_domain_name = ENV['OS_USER_DOMAIN_ID'] 19 | os.project_domain_name = ENV['OS_PROJECT_DOMAIN_ID'] 20 | os.username = ENV['OS_USERNAME'] 21 | os.password = ENV['OS_PASSWORD'] 22 | os.region = ENV['OS_REGION_NAME'] 23 | os.floating_ip_pool = ENV['OS_FLOATING_IP_POOL'] 24 | os.floating_ip_pool_always_allocate = (ENV['OS_FLOATING_IP_ALWAYS_ALLOCATE'] == 'true') 25 | os.flavor = ENV['OS_FLAVOR'] 26 | os.image = ENV['OS_IMAGE'] 27 | os.networks << ENV['OS_NETWORK'] 28 | 29 | ov.nfs.functional = false 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /samples/07_insert_key_false.001.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load test_helper 4 | 5 | @test "07 - With config.ssh.insert_key = false" { 6 | title "$BATS_TEST_DESCRIPTION" 7 | 8 | export VAGRANT_CWD=$BATS_TEST_DIRNAME/07_insert_key_false 9 | 10 | run exec_vagrant up 11 | flush_out 12 | [ "$status" -eq 0 ] 13 | 14 | run exec_vagrant ssh -c "true" 15 | flush_out 16 | [ "$status" -eq 0 ] 17 | 18 | run exec_vagrant destroy 19 | flush_out 20 | [ "$status" -eq 0 ] 21 | } 22 | -------------------------------------------------------------------------------- /samples/07_insert_key_false/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | Vagrant.configure('2') do |config| 5 | 6 | config.ssh.username = ENV['OS_SSH_USERNAME'] 7 | config.ssh.insert_key = false 8 | config.ssh.private_key_path = './ssh_key' 9 | 10 | config.vm.provider :openstack do |os, ov| 11 | os.server_name = '06_insert_key_false' 12 | os.openstack_auth_url = ENV['OS_AUTH_URL'] 13 | os.tenant_name = ENV['OS_TENANT_NAME'] 14 | os.username = ENV['OS_USERNAME'] 15 | os.password = ENV['OS_PASSWORD'] 16 | os.region = ENV['OS_REGION_NAME'] 17 | os.floating_ip_pool = ENV['OS_FLOATING_IP_POOL'] 18 | os.floating_ip_pool_always_allocate = true 19 | os.flavor = ENV['OS_FLAVOR'] 20 | os.image = ENV['OS_IMAGE'] 21 | os.networks << ENV['OS_NETWORK'] 22 | os.user_data = """#!/bin/bash 23 | 24 | set -x 25 | mkdir -p /home/ubuntu/.ssh 26 | echo '' >> /home/ubuntu/.ssh/authorized_keys 27 | echo 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDENu9FoAOxYgz2aUc7zF4ODbdLKsSS864AMrSlOnbIrcMuHgsgz23fMIIEoiaw22ztH2CcO5pwCqfEPpQGXhaenv51i6/m8/rGy9sb201eIPDpOd/5BOjK0Da/w3hbfeMcq9VJhhq3C7J04Ds9BVrHBlYDeWN1bZQBt+ut8jWxgnR5hUBJopKgl/ypDP6vLZ2lyCU7MZVbPKwZJKei2UdLGtPrkkBcdSsfj9I+4cBVC7bYA8eEXTtvV44iYPrqQ9Oa1GIFD3clCUBUccI3SGG4RQ8E+Rzmbp1LnKM4tIV3iOMkwumtyaehJjzsYYdzl3H3g85sjliEhMvLKMZyZknv vagrant' >> /home/ubuntu/.ssh/authorized_keys 28 | """ 29 | 30 | ov.nfs.functional = false 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /samples/07_insert_key_false/ssh_key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAxDbvRaADsWIM9mlHO8xeDg23SyrEkvOuADK0pTp2yK3DLh4L 3 | IM9t3zCCBKImsNts7R9gnDuacAqnxD6UBl4Wnp7+dYuv5vP6xsvbG9tNXiDw6Tnf 4 | +QToytA2v8N4W33jHKvVSYYatwuydOA7PQVaxwZWA3ljdW2UAbfrrfI1sYJ0eYVA 5 | SaKSoJf8qQz+ry2dpcglOzGVWzysGSSnotlHSxrT65JAXHUrH4/SPuHAVQu22APH 6 | hF07b1eOImD66kPTmtRiBQ93JQlAVHHCN0hhuEUPBPkc5m6dS5yjOLSFd4jjJMLp 7 | rcmnoSY87GGHc5dx94PObI5YhITLyyjGcmZJ7wIDAQABAoIBABoP9e44l+T60h/0 8 | 0epmdePF/J+2WY/vYCBUm9wKjPaQ5gj9IptEEiMALXTyGNOLAcfpUArQe+3/7FLL 9 | wRurabO+CKySVlYF+DxpnXvwwDENjRJdhptsBIPT7kHAc/8rFHydcmImtuuzWQvf 10 | MN8lE+61r70cZZKWcH9d76cWMf2Wy4Ut96APBbhwg5+9z5rWVSdLmLplboNKLi6B 11 | /u25dhddk5RelVC7anL7YV8wNBOKgTAFedUee+sW8dotIBTUxQS8rUSdV1Ogz2H/ 12 | VsDW5w/ZfHdeoD5QD2up2oj2LhZ9FSTrTvYP+QVNgkecoF+jJ7bE3I7cZ6hNrVSV 13 | XXYiD7ECgYEA+QCpqKF3D1fB1fGSru0B7dgc4APJFdFwD+JJTJkwLFnTJpjsPmj4 14 | 9PEb+xySeSrL7yw/8aCTRKgKUqVE8dEPPIm9tYqdBeqW9pCJVZAASm/9TTYxIdGH 15 | DEJjTTCXBKGJhAioyeizLlOKSTuktsxKtMH8fbUqa83hPYPv5F6qsTcCgYEAybqD 16 | KslLT05qzwr0lNlgLlRHT4J0JYcDtSlpeTgON3lGLce/dIRUVBuEvWKM0mvXmBKd 17 | snbwbdYRmAiJC3tYeQagzgZTJ0vpNyBCKCBsxB9qeqTIhYI8aubHV+PN5K/onsdk 18 | gF2SfI80JxAzLyoQNNCzH8n6Pr3zpUSVmz896QkCgYAZZizoK5g2c3AtP5gDyd9B 19 | gKLpYtl3JUb2B9IMQy/7xinYrQ6kCxch/Roqki6Kt4fX3L6lCgkbnrx2067lr7qL 20 | JU25n+sGeHhvrCymEqtE7Il8/8MhPPs2j+sLK6Kr8UJSuoNlHRxx5Emd0LyrfMk+ 21 | CO+Vbw5lsrmACd5FgDaPHwKBgQCqpJl8QhbwmTcEuRG4MEGKL0S7X3D8pb9fKg7M 22 | ubxc9LuG0meEGsRy+WxJ/+l7vjn7rBO1+aLcZq8vWlkDW221wwOYBAWnQjGfriwI 23 | YZVgOesKoAX32fldvKZ4FH4Aq0UtuyGq7HyT3VeSi8OgbAaVhs1fYK3NEUXfUar0 24 | ToUCcQKBgQDCbZZPh2nQKZlTssMNeCWiRkInJkcnzJxGjlAyxN+Wz3UT6VfiYIxA 25 | A/ogm7fsppbIsGh2cl38J6lCcSsWCgu2rNVvuOV8qNjHY8jeQkxZQzmdZgyn41n5 26 | 5W5SQj1DcpzVOH6LGWgBo1FctLzX6j6EXNCgAVVaCRLU+8jHIDtZmw== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /samples/09_with_http_proxy.001.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load test_helper 4 | 5 | @test "09 - With HTTP Proxy" { 6 | title "$BATS_TEST_DESCRIPTION" 7 | 8 | export VAGRANT_CWD=$BATS_TEST_DIRNAME/09_with_http_proxy 9 | 10 | run exec_vagrant up 11 | flush_out 12 | [ "$status" -eq 0 ] 13 | 14 | run exec_vagrant ssh -c "true" 15 | flush_out 16 | [ "$status" -eq 0 ] 17 | 18 | run exec_vagrant destroy 19 | flush_out 20 | [ "$status" -eq 0 ] 21 | } 22 | -------------------------------------------------------------------------------- /samples/09_with_http_proxy/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | Vagrant.configure('2') do |config| 5 | 6 | config.ssh.username = ENV['OS_SSH_USERNAME'] 7 | 8 | config.vm.provider :openstack do |os, ov| 9 | os.http.proxy = 'http://openstack:3128' 10 | 11 | os.server_name = '09_with_http_proxy' 12 | os.openstack_auth_url = ENV['OS_AUTH_URL'] 13 | os.tenant_name = ENV['OS_TENANT_NAME'] 14 | os.username = ENV['OS_USERNAME'] 15 | os.password = ENV['OS_PASSWORD'] 16 | os.region = ENV['OS_REGION_NAME'] 17 | os.floating_ip_pool = ENV['OS_FLOATING_IP_POOL'] 18 | os.floating_ip_pool_always_allocate = (ENV['OS_FLOATING_IP_ALWAYS_ALLOCATE'] == 'true') 19 | os.flavor = ENV['OS_FLAVOR'] 20 | os.image = ENV['OS_IMAGE'] 21 | os.networks << ENV['OS_NETWORK'] 22 | 23 | ov.nfs.functional = false 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /samples/README.md: -------------------------------------------------------------------------------- 1 | # Samples 2 | 3 | This directory contains Vagrantfile examples for the vagrant-openstack-provider. 4 | 5 | # Running samples 6 | 7 | A [bats](https://github.com/sstephenson/bats) script is available to run all samples as a test 8 | suite. 9 | 10 | ## Prerequisites 11 | 12 | ### Script 13 | 14 | To run the tests suite you need the following tools: 15 | 16 | * Bats 17 | * OpenStack CLI 18 | 19 | Values matching your OpenStack information must be set in `openrc-common.sh`, `openrc-keystone-v2.sh` and `openrc-keystone-v3.sh`. 20 | 21 | ### OpenStack 22 | 23 | You need an OpenStack project. In order to run the whole test suite, this project should be accessible using Keystone v2 and v3 credentials. 24 | 25 | In the projet, the following topology is supposed to exists: 26 | 27 | * A router connected to a public network for public access through floating IPs 28 | * A private network connected to the router 29 | * Security group rules and/or firewall rules to allow incomming SSH connections to machines on the private network via floating IPs 30 | 31 | ## Run 32 | 33 | To run a single test, basically execute the desired script 34 | 35 | ```bash 36 | $ ./01_simple.001.bats 37 | ``` 38 | 39 | To run several test run bats explicitly with tests script in argument 40 | 41 | ```bash 42 | $ bats 01_simple.001.bats 01_simple.002.bats 43 | ``` 44 | 45 | Or to run the whole tests suite 46 | 47 | ```bash 48 | $ bats *.bats 49 | ``` 50 | -------------------------------------------------------------------------------- /samples/openrc-common.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export OS_IMAGE_API_VERSION=2 4 | export OS_REGION_NAME=RegionOne 5 | 6 | export OS_FLOATING_IP_ALWAYS_ALLOCATE=false 7 | export OS_FLOATING_IP_POOL_NAME=public 8 | export OS_FLOATING_IP_POOL_ID=5cccfde8-ddae-4feb-9a97-ca6532d1577f 9 | export OS_FLOATING_IP_POOL=${OS_FLOATING_IP_POOL_NAME} 10 | 11 | export OS_FLAVOR=m1.small 12 | export OS_IMAGE=Ubuntu-14.04 13 | export OS_NETWORK=net 14 | export OS_SSH_USERNAME=ubuntu 15 | -------------------------------------------------------------------------------- /samples/openrc-keystone-v2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | 5 | export OS_AUTH_URL=http://openstack:5000/v2.0 6 | export OS_IDENTITY_API_VERSION=2 7 | export OS_TENANT_NAME=admin 8 | export OS_USERNAME=admin 9 | export OS_PASSWORD=admin 10 | 11 | source ${script_dir}/openrc-common.sh 12 | -------------------------------------------------------------------------------- /samples/openrc-keystone-v3.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | 5 | export OS_AUTH_URL=http://openstack:5000/v3 6 | export OS_IDENTITY_API_VERSION=3 7 | export OS_PROJECT_DOMAIN_ID=default 8 | export OS_USER_DOMAIN_ID=default 9 | export OS_PROJECT_NAME=admin 10 | export OS_USERNAME=admin 11 | export OS_PASSWORD=admin 12 | 13 | source ${script_dir}/openrc-common.sh 14 | -------------------------------------------------------------------------------- /samples/test_helper.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BATS_OUT_LOG=$BATS_TEST_DIRNAME/test.log 4 | 5 | setup() { 6 | title "Setup" 7 | 8 | unset_openstack_env 9 | export VAGRANT_OPENSTACK_LOG=debug 10 | 11 | source $BATS_TEST_DIRNAME/openrc-keystone-v2.sh 12 | 13 | delete_all_floating_ip >> $BATS_OUT_LOG 14 | cd $BATS_TEST_DIRNAME/../source 15 | } 16 | 17 | teardown() { 18 | title "Teardown" 19 | 20 | bundle exec vagrant destroy >> $BATS_OUT_LOG 21 | 22 | delete_all_floating_ip >> $BATS_OUT_LOG 23 | cd $BATS_TEST_DIRNAME 24 | } 25 | 26 | unset_openstack_env() { 27 | for v in $(env | grep OS_ | sed -e "s/\(.*\)=.*/\1/g") ; do unset $v ; done 28 | } 29 | 30 | delete_all_floating_ip() { 31 | for ip in $(openstack floating ip list | awk '/\| [a-f0-9]/{ print $2 }') ; do 32 | openstack floating ip delete ${ip} 33 | done 34 | } 35 | 36 | allocate_4_floating_ip() { 37 | for ip in {1..4} ; do 38 | openstack floating ip create ${OS_FLOATING_IP_POOL} 39 | done 40 | } 41 | 42 | title() { 43 | { 44 | echo "" 45 | echo "###########################################################" 46 | echo "### $1" 47 | echo "###########################################################" 48 | echo "" 49 | } >> $BATS_OUT_LOG 50 | } 51 | 52 | flush_out() { 53 | { 54 | echo "" 55 | printf "%s\n" "${lines[@]}" 56 | } >> $BATS_OUT_LOG 57 | } 58 | 59 | exec_vagrant() { 60 | vagrant $* 61 | } 62 | -------------------------------------------------------------------------------- /source/.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.iml 3 | *.rbc 4 | .bundle 5 | .config 6 | .vagrant 7 | .yardoc 8 | Gemfile.lock 9 | gemfiles/*.lock 10 | InstalledFiles 11 | _yardoc 12 | coverage 13 | doc/ 14 | lib/bundler/man 15 | pkg 16 | rdoc 17 | spec/reports 18 | test/tmp 19 | test/version_tmp 20 | tmp 21 | .venv 22 | .idea/ 23 | -------------------------------------------------------------------------------- /source/.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | Exclude: 3 | - '.bundle/**/*' 4 | - 'out/**/*' 5 | - '**/Vagrantfile' 6 | DisplayCopNames: true 7 | 8 | Style/FileName: 9 | Enabled: false 10 | 11 | Style/Encoding: 12 | Enabled: false 13 | 14 | Style/Documentation: 15 | Enabled: false 16 | 17 | Style/ClassVars: 18 | Enabled: false 19 | 20 | Metrics/ClassLength: 21 | Max: 300 22 | 23 | Metrics/CyclomaticComplexity: 24 | Severity: warning 25 | Max: 15 26 | 27 | Metrics/MethodLength: 28 | Max: 65 29 | 30 | Metrics/LineLength: 31 | Max: 150 32 | 33 | Metrics/ParameterLists: 34 | Max: 6 35 | 36 | Metrics/AbcSize: 37 | Max: 110 38 | 39 | Metrics/PerceivedComplexity: 40 | Max: 45 41 | -------------------------------------------------------------------------------- /source/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | 5 | group :development do 6 | gem 'vagrant', git: 'https://github.com/mitchellh/vagrant.git', tag: 'v2.3.3' 7 | gem 'appraisal', '1.0.0' 8 | gem 'rubocop', '0.29.0', require: false 9 | gem 'coveralls', require: false 10 | gem 'rspec-its' 11 | end 12 | 13 | group :debug do 14 | gem 'byebug' 15 | end 16 | -------------------------------------------------------------------------------- /source/RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release process 2 | 3 | This is vagrant-openstack-provider's current release process, documented so people know what is 4 | currently done. 5 | 6 | ## Prepare the release 7 | 8 | * Update the version in "lib/vagrant-openstack-provider/version.rb" 9 | * Update the version in CHANGELOG.md 10 | * Use "rake release". This will make sure to tag that commit and push it RubyGems. 11 | * Update the version again in both files to a dev version for working again. 12 | 13 | The CHANGELOG.md should be maintained in a similar format to Vagrant: 14 | 15 | https://github.com/mitchellh/vagrant/blob/master/CHANGELOG.md 16 | -------------------------------------------------------------------------------- /source/Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'bundler/setup' 3 | require 'rspec/core/rake_task' 4 | require 'rubocop/rake_task' 5 | 6 | # Immediately sync all stdout so that tools like buildbot can 7 | # immediately load in the output. 8 | $stdout.sync = true 9 | $stderr.sync = true 10 | 11 | # Change to the directory of this file. 12 | Dir.chdir(File.expand_path('../', __FILE__)) 13 | 14 | # This installs the tasks that help with gem creation and 15 | # publishing. 16 | Bundler::GemHelper.install_tasks 17 | 18 | # Install the `spec` task so that we can run tests. 19 | RSpec::Core::RakeTask.new 20 | 21 | # Install the `rubocop` task 22 | RuboCop::RakeTask.new 23 | 24 | # Default task is to run the unit tests 25 | task default: %w(rubocop spec) 26 | -------------------------------------------------------------------------------- /source/Vagrantfile: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider' 2 | 3 | Vagrant.configure('2') do |config| 4 | 5 | config.vm.box = 'openstack' 6 | 7 | config.ssh.username = ENV['OS_SSH_USERNAME'] 8 | 9 | config.vm.provider :openstack do |os| 10 | os.openstack_auth_url = ENV['OS_AUTH_URL'] 11 | os.tenant_name = ENV['OS_TENANT_NAME'] 12 | os.username = ENV['OS_USERNAME'] 13 | os.password = ENV['OS_PASSWORD'] 14 | os.floating_ip_pool = ENV['OS_FLOATING_IP_POOL'] 15 | os.flavor = ENV['OS_FLAVOR'] 16 | os.image = ENV['OS_IMAGE'] 17 | end 18 | 19 | config.vm.provision "shell", inline: "echo 'ok' > ~/provision" 20 | end 21 | -------------------------------------------------------------------------------- /source/dummy.box: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ggiamarchi/vagrant-openstack-provider/b6a51c1b7d382a9a1ef088ad8a9c6af72d3964a2/source/dummy.box -------------------------------------------------------------------------------- /source/example_box/README.md: -------------------------------------------------------------------------------- 1 | # Vagrant Openstack Cloud Example Box 2 | 3 | Vagrant providers each require a custom provider-specific box format. 4 | This folder shows the example contents of a box for the `openstack` provider. 5 | To turn this into a box: 6 | 7 | ``` 8 | $ tar cvzf openstack.box ./metadata.json ./Vagrantfile 9 | ``` 10 | 11 | This box works by using Vagrant's built-in Vagrantfile merging to setup 12 | defaults for Openstack. These defaults can easily be overwritten by higher-level 13 | Vagrantfiles (such as project root Vagrantfiles). 14 | -------------------------------------------------------------------------------- /source/example_box/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "provider": "openstack" 3 | } 4 | -------------------------------------------------------------------------------- /source/functional_tests/Vagrantfile: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider' 2 | 3 | Vagrant.configure('2') do |config| 4 | 5 | config.vm.box = 'openstack' 6 | config.vm.box_url = 'https://github.com/ggiamarchi/vagrant-openstack/raw/master/source/dummy.box' 7 | config.vm.boot_timeout = ENV['OS_SSH_TIMEOUT'].to_i 8 | 9 | config.ssh.private_key_path = ENV['OS_KEYPAIR_PRIVATE_KEY'] 10 | config.ssh.pty = true 11 | config.ssh.shell = ENV['OS_SSH_SHELL'] 12 | 13 | config.vm.provider :openstack do |os| 14 | os.username = ENV['OS_USERNAME'] 15 | os.password = ENV['OS_PASSWORD'] 16 | os.openstack_auth_url = ENV['OS_AUTH_URL'] 17 | os.openstack_compute_url = ENV['OS_COMPUTE_URL'] 18 | os.openstack_network_url = ENV['OS_NETWORK_URL'] 19 | os.tenant_name = ENV['OS_TENANT_NAME'] 20 | os.sync_method = ENV['OS_SYNC_METHOD'] 21 | os.flavor = ENV['OS_FLAVOR'] 22 | os.image = ENV['OS_IMAGE'] 23 | os.ssh_username = ENV['OS_SSH_USERNAME'] 24 | os.keypair_name = ENV['OS_KEYPAIR_NAME'] 25 | os.floating_ip_pool = ENV['OS_FLOATING_IP_POOL'] 26 | end 27 | 28 | config.vm.define 'test-basic' do |test| 29 | test.vm.provider :openstack do |os| 30 | os.floating_ip = ENV['OS_FLOATING_IP'] 31 | os.floating_ip_pool = nil 32 | end 33 | test.vm.provision 'shell', inline: 'echo "SUCCESS" > /tmp/test_shell_provision' 34 | end 35 | 36 | config.vm.define 'test-floating-ip-pool' do |test| 37 | test.vm.provider :openstack do |os| 38 | os.floating_ip = nil 39 | os.floating_ip_pool = ENV['OS_FLOATING_IP_POOL'] 40 | end 41 | test.vm.provision 'shell', inline: 'echo "SUCCESS" > /tmp/test_shell_provision' 42 | end 43 | 44 | config.vm.define 'test-ssh-public-key-path' do |test| 45 | test.vm.provider :openstack do |os| 46 | os.keypair_name = nil 47 | os.public_key_path = ENV['OS_PUBLIC_KEY_PATH'] 48 | end 49 | test.vm.provision 'shell', inline: 'echo "SUCCESS" > /tmp/test_shell_provision' 50 | end 51 | 52 | config.vm.define 'test-availabilty-zone' do |test| 53 | test.vm.provider :openstack do |os| 54 | os.availability_zone = ENV['OS_AZ'] 55 | end 56 | test.vm.provision 'shell', inline: 'echo "SUCCESS" > /tmp/test_shell_provision' 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /source/functional_tests/keys/vagrant-openstack: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEArKPKCXfOr8pjInYeDfGOfKC5JS8jWGgf5OqD0SlvRpdWEKa6 3 | KR6gDL6rVVFU4z7T5lECzeCp+P4uXwYhqXM4TodEccjPkMLDgR5jt+Fd9ixtlVYR 4 | Gj5vuAv1a7gk4zVr4M82oOKnTriTqTrFGZL3YG3XyxKftoTZyDWsI4nAbfMoup/e 5 | d54zHY6gApgDGBg+XlJz5Ai4AJT2YtCwWyu4v/zuDcEtMBDbioI8jpWkKTycFMDw 6 | W8f5Cwxe3bMCw9sasC6+FUI97ac5itvnE6/LZvcftYjXQF0oakz3ujZ0/gN0rhpe 7 | pswU2ODSkXWxOMBYS0PXZz9N2r3jzGXa5h1EfwIDAQABAoIBACGaL7TwENa+edU3 8 | UFo+bhFr5x2Js0N94NdZnhaUDgibZDERgqKGR3izk/2jOvaQQAZQNk+ELxE7yCLO 9 | uraUqpz+TyAmieAPSKZVF+uufe9wblPm0KVfCbe0/CvfR67BsyGqs2NVOmNkIbmK 10 | qtpzdJrcRmhMU7He4dTKPZsdMMs1esmWK3+uGgtfQTSaCi7rTxsCMn+Ob+lxvfvG 11 | QS2Ehb63jyb+8L8PyRqnunK9iBpdhuLHN8tGeDzmrCVz/ziRenslfXq1Ng7pxhUQ 12 | gyXWjlMoRibt9bcsSt1LcaqZMAeiiRnyUlaqyvtkhSb+yzZyHQZs90DgxRVH40Ya 13 | ebq9FcECgYEA5eXApNgi2iLDWMIGISO9uPDxzR5i4aDjIyHGv1uos/hWwJiX864K 14 | N23WLLna2LyIAZaEzUi76cPReFza7lxDtl/OeRDgf7yCPWZdH9OhyAeAtVVOu4eN 15 | iX5dee/GW5Mmu5xBUXo4z0TETqbey1fWHeJNqaA5W9mWkBKrKnT2aIcCgYEAwD3F 16 | /yUb8fS/Tj02oh2qb3clIwEIgdh6C38/d2J1OduICVzLoMhq0nZnZ1zMPNtLA/g2 17 | TjZ70EY1VyDAOWaIo+FGU/HIuLsP2jWKvDSKhJUx/oBBPWpCxqBZefoQhKZLkbmO 18 | Tw+SrUBQM7r8pL/5ptocW0HDwjL8Sbs4KfSWWkkCgYBSLuPDChDLcgnrPND8H86+ 19 | wkNuVCJ9DgqkkHqABcA1Nd2tU99eGSVF01nw+y+ksyDbkHdA+3NRidLj+C27b/g0 20 | xeMFnGbkwvq8AE/iBMGcxDHaoPhYSYjrUeUQpgp+ygfaoW0oN0z/q1GR3E1g27GL 21 | VU72CHT4xLvyHPpbXxyHGQKBgAXO1/iJanq45j664rerJccQVnLkSRmDLMzEH2q/ 22 | 8sK4uzdtMkm9RFzvbthUmWcNSQrpqNpcEwmL1Xi4aJZTXrV0zOckWugZ3rS9AWAG 23 | RlkTGNuTjGUKnNHbblidEXqwe3//lykUU14gn0uwzok7s5My68BmEEABwlWH7n52 24 | AUThAoGBANwwj2WKKu6l66fyQPFZu0cJTfjhiZt1WH17mHAAtL2TpLnDJiPZYbzJ 25 | 8WRqCWrww+IVq1yxBS6LhAGMuHTfxFWZGKSPxf9jOM87RKJbPkiBDHoFXznjkjsn 26 | tXk6jmn+6bCmwkgfzVypN3GTyEMhVgQPMJNqlMO9Op5ZAAKuyi31 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /source/functional_tests/keys/vagrant-openstack.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCso8oJd86vymMidh4N8Y58oLklLyNYaB/k6oPRKW9Gl1YQpropHqAMvqtVUVTjPtPmUQLN4Kn4/i5fBiGpczhOh0RxyM+QwsOBHmO34V32LG2VVhEaPm+4C/VruCTjNWvgzzag4qdOuJOpOsUZkvdgbdfLEp+2hNnINawjicBt8yi6n953njMdjqACmAMYGD5eUnPkCLgAlPZi0LBbK7i//O4NwS0wENuKgjyOlaQpPJwUwPBbx/kLDF7dswLD2xqwLr4VQj3tpzmK2+cTr8tm9x+1iNdAXShqTPe6NnT+A3SuGl6mzBTY4NKRdbE4wFhLQ9dnP03avePMZdrmHUR/ julienvey@MacBook-Air-de-Julien.local 2 | -------------------------------------------------------------------------------- /source/functional_tests/run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export VAGRANT_OPENSTACK_LOG=debug 4 | 5 | export OS_SSH_TIMEOUT=600 6 | export OS_SYNC_METHOD=none 7 | export OS_SSH_SHELL=bash 8 | export OS_SSH_USERNAME= 9 | export OS_SERVER_NAME= 10 | export OS_IMAGE= 11 | 12 | ERROR_STATE=0 13 | 14 | cat > /tmp/images_with_ssh_user < /tmp/vagrant_machines <&2 && exit 1 35 | level=$1 ; shift 36 | action=$1 ; shift 37 | printf "$(date '+%Y-%m-%d %H:%M:%S') | %10s | %10s | %s\n" "${level}" "${action}" "$*" | tee -a test.log 38 | } 39 | 40 | # 41 | # $1 - Action (e.g. UP, SSH, DESTROY) 42 | # $* - Text 43 | # 44 | function logInfo() { 45 | action=$1 46 | shift 47 | log INFO "${action}" "$*" 48 | } 49 | 50 | # 51 | # $1 - Action (e.g. UP, SSH, DESTROY) 52 | # $* - Text 53 | # 54 | function logError() { 55 | action=$1 56 | shift 57 | log ERROR "${action}" "$*" 58 | ERROR_STATE=1 59 | } 60 | 61 | # 62 | # $1 - Action (e.g. UP, SSH, DESTROY) 63 | # $* - Text 64 | # 65 | function logSuccess() { 66 | action=$1 67 | shift 68 | log SUCCESS "${action}" "$*" 69 | } 70 | 71 | runSingleTest() { 72 | if [ -d ".vagrant" ]; then 73 | rm -rf .vagrant 74 | fi 75 | machine=${1} 76 | 77 | testSummary="${OS_SERVER_NAME} - ${OS_IMAGE} - ${OS_SSH_USERNAME}" 78 | 79 | logInfo 'START' "${testSummary}" 80 | 81 | bundle exec vagrant up "${machine}" --provider openstack 2>&1 | tee -a "${OS_SERVER_NAME}_up.log" 82 | if [ "${PIPESTATUS[0]}" -ne 0 ] ; then 83 | logError 'UP' "${testSummary}" 84 | else 85 | logSuccess 'UP' "${testSummary}" 86 | bundle exec vagrant ssh "${machine}" -c "cat /tmp/test_shell_provision" 2>&1 | tee -a "${OS_SERVER_NAME}_ssh.log" 87 | if [ "${PIPESTATUS[0]}" -ne 0 ] ; then 88 | logError 'SSH' "${testSummary}" 89 | else 90 | logSuccess 'SSH' "${testSummary}" 91 | fi 92 | fi 93 | 94 | bundle exec vagrant destroy "${machine}" 2>&1 | tee -a "${OS_SERVER_NAME}_destroy.log" 95 | if [ "${PIPESTATUS[0]}" -ne 0 ] ; then 96 | logError 'DESTROY' "${testSummary}" 97 | else 98 | logSuccess 'DESTROY' "${testSummary}" 99 | fi 100 | 101 | logInfo 'END' "${testSummary}" 102 | 103 | } 104 | 105 | # 106 | # $1 - Instance name prefix 107 | # $2 - Floating IP tu use 108 | # 109 | function runAllTests() { 110 | ip=${1} 111 | i=1 112 | rm -f test.log "${name}_*.log" 113 | touch test.log 114 | nbTests=$(wc -l < /tmp/images_with_ssh_user) 115 | for (( i=1 ; i<=nbTests ; i++ )) ; do 116 | #TODO(vagrant status does not support providers, see https://github.com/mitchellh/vagrant/issues/4173) 117 | #for machine in $(bundle exec vagrant status | tail -n +8 | head -n -4 | awk '{print $1}') ; do 118 | while IFS= read -r machine 119 | do 120 | currentTest=$(head -n ${i} < /tmp/images_with_ssh_user | tail -n 1) 121 | export OS_SERVER_NAME="${machine}_${i}" 122 | export OS_IMAGE=$(echo "${currentTest}" | cut -f1 -d";") 123 | export OS_FLOATING_IP="${ip}" 124 | export OS_SSH_USERNAME=$(echo "${currentTest}" | cut -f2 -d";") 125 | runSingleTest "${machine}" 126 | done < /tmp/vagrant_machines 127 | done 128 | } 129 | 130 | runAllTests "${OS_FLOATING_IP}" 131 | 132 | echo '' 133 | echo '################################################################################################' 134 | echo '# Report summary #' 135 | echo '################################################################################################' 136 | echo '' 137 | cat test.log 138 | echo '' 139 | echo '################################################################################################' 140 | echo '' 141 | 142 | exit ${ERROR_STATE} 143 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider.rb: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | 3 | require 'vagrant-openstack-provider/plugin' 4 | require 'vagrant-openstack-provider/logging' 5 | 6 | module VagrantPlugins 7 | module Openstack 8 | lib_path = Pathname.new(File.expand_path('../vagrant-openstack-provider', __FILE__)) 9 | autoload :Errors, lib_path.join('errors') 10 | 11 | # This initializes the i18n load path so that the plugin-specific 12 | # translations work. 13 | def self.init_i18n 14 | I18n.load_path << File.expand_path('locales/en.yml', source_root) 15 | I18n.reload! 16 | end 17 | 18 | def self.init_logging 19 | Logging.init 20 | end 21 | 22 | # This returns the path to the source of this plugin. 23 | # 24 | # @return [Pathname] 25 | def self.source_root 26 | @source_root ||= Pathname.new(File.expand_path('../../', __FILE__)) 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/action/abstract_action.rb: -------------------------------------------------------------------------------- 1 | require 'colorize' 2 | 3 | module VagrantPlugins 4 | module Openstack 5 | module Action 6 | class AbstractAction 7 | def call(env) 8 | execute(env) 9 | # rubocop:disable Style/SpecialGlobalVars 10 | # rubocop:disable Lint/RescueException 11 | rescue Errors::VagrantOpenstackError, SystemExit, Interrupt => e 12 | raise e 13 | rescue Exception => e 14 | puts I18n.t('vagrant_openstack.global_error').red unless e.message && e.message.start_with?('Catched Error:') 15 | raise $!, "Catched Error: #{$!}", $!.backtrace 16 | end 17 | # rubocop:enable Lint/RescueException 18 | # rubocop:enable Style/SpecialGlobalVars 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/action/connect_openstack.rb: -------------------------------------------------------------------------------- 1 | require 'log4r' 2 | require 'json' 3 | 4 | require 'vagrant-openstack-provider/client/openstack' 5 | require 'vagrant-openstack-provider/client/request_logger' 6 | require 'vagrant-openstack-provider/action/abstract_action' 7 | require 'vagrant-openstack-provider/catalog/openstack_catalog' 8 | 9 | module VagrantPlugins 10 | module Openstack 11 | module Action 12 | class ConnectOpenstack < AbstractAction 13 | include VagrantPlugins::Openstack::HttpUtils::RequestLogger 14 | include VagrantPlugins::Openstack::Catalog 15 | 16 | def initialize(app, env, catalog_reader = OpenstackCatalog.new) 17 | @app = app 18 | @logger = Log4r::Logger.new('vagrant_openstack::action::connect_openstack') 19 | @catalog_reader = catalog_reader 20 | env[:openstack_client] = VagrantPlugins::Openstack 21 | end 22 | 23 | def execute(env) 24 | client = env[:openstack_client] 25 | if client.session.token.nil? 26 | catalog = client.keystone.authenticate(env) 27 | @catalog_reader.read(env, catalog) 28 | override_endpoint_catalog_with_user_config(env) 29 | check_configuration(env) 30 | log_endpoint_catalog(env) 31 | end 32 | @app.call(env) unless @app.nil? 33 | end 34 | 35 | private 36 | 37 | def override_endpoint_catalog_with_user_config(env) 38 | client = env[:openstack_client] 39 | config = env[:machine].provider_config 40 | endpoints = client.session.endpoints 41 | endpoints[:compute] = config.openstack_compute_url unless config.openstack_compute_url.nil? 42 | endpoints[:network] = config.openstack_network_url unless config.openstack_network_url.nil? 43 | endpoints[:volume] = config.openstack_volume_url unless config.openstack_volume_url.nil? 44 | endpoints[:image] = config.openstack_image_url unless config.openstack_image_url.nil? 45 | endpoints.delete_if { |_, value| value.nil? || value.empty? } 46 | end 47 | 48 | def check_configuration(env) 49 | fail Errors::MissingNovaEndpoint unless env[:openstack_client].session.endpoints.key? :compute 50 | end 51 | 52 | def log_endpoint_catalog(env) 53 | env[:openstack_client].session.endpoints.each do |key, value| 54 | @logger.info(" -- #{key.to_s.ljust 15}: #{value}") 55 | end 56 | end 57 | end 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/action/create_stack.rb: -------------------------------------------------------------------------------- 1 | require 'log4r' 2 | require 'socket' 3 | require 'timeout' 4 | require 'sshkey' 5 | require 'yaml' 6 | 7 | require 'vagrant-openstack-provider/config_resolver' 8 | require 'vagrant-openstack-provider/utils' 9 | require 'vagrant-openstack-provider/action/abstract_action' 10 | require 'vagrant/util/retryable' 11 | 12 | module VagrantPlugins 13 | module Openstack 14 | module Action 15 | class CreateStack < AbstractAction 16 | @@is_created = false 17 | 18 | def initialize(app, _env) 19 | @app = app 20 | @logger = Log4r::Logger.new('vagrant_openstack::action::create_stack') 21 | end 22 | 23 | def execute(env) 24 | if @@is_created 25 | @app.call(env) 26 | return 27 | end 28 | 29 | @logger.info 'Start create stacks action' 30 | 31 | config = env[:machine].provider_config 32 | 33 | heat = env[:openstack_client].heat 34 | 35 | config.stacks.each do |stack| 36 | env[:ui].info(I18n.t('vagrant_openstack.create_stack')) 37 | env[:ui].info(" -- Stack Name : #{stack[:name]}") 38 | env[:ui].info(" -- Template : #{stack[:template]}") 39 | 40 | create_opts = { 41 | name: stack[:name], 42 | template: YAML.load_file(stack[:template]) 43 | } 44 | 45 | stack_id = heat.create_stack(env, create_opts) 46 | 47 | file_path = "#{env[:machine].data_dir}/stack_#{stack[:name]}_id" 48 | File.write(file_path, stack_id) 49 | 50 | waiting_for_stack_to_be_created(env, stack[:name], stack_id) 51 | end unless config.stacks.nil? 52 | 53 | @@is_created = true 54 | @app.call(env) 55 | end 56 | 57 | private 58 | 59 | def waiting_for_stack_to_be_created(env, stack_name, stack_id, retry_interval = 3) 60 | @logger.info "Waiting for the stack with id #{stack_id} to be built..." 61 | env[:ui].info(I18n.t('vagrant_openstack.waiting_for_stack')) 62 | config = env[:machine].provider_config 63 | Timeout.timeout(config.stack_create_timeout, Errors::Timeout) do 64 | stack_status = 'CREATE_IN_PROGRESS' 65 | until stack_status == 'CREATE_COMPLETE' 66 | @logger.debug('Waiting for stack to be CREATED') 67 | stack_status = env[:openstack_client].heat.get_stack_details(env, stack_name, stack_id)['stack_status'] 68 | fail Errors::StackStatusError, stack: stack_id if stack_status == 'CREATE_FAILED' 69 | sleep retry_interval 70 | end 71 | end 72 | end 73 | end 74 | end 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/action/delete_server.rb: -------------------------------------------------------------------------------- 1 | require 'log4r' 2 | 3 | require 'vagrant-openstack-provider/action/abstract_action' 4 | 5 | module VagrantPlugins 6 | module Openstack 7 | module Action 8 | # This deletes the running server, if there is one. 9 | class DeleteServer < AbstractAction 10 | def initialize(app, _env) 11 | @app = app 12 | @logger = Log4r::Logger.new('vagrant_openstack::action::delete_server') 13 | end 14 | 15 | def execute(env) 16 | if env[:machine].id 17 | @logger.info "Deleting server #{env[:machine].id}..." 18 | env[:ui].info(I18n.t('vagrant_openstack.deleting_server')) 19 | env[:openstack_client].nova.delete_server(env, env[:machine].id) 20 | env[:openstack_client].nova.delete_keypair_if_vagrant(env, env[:machine].id) 21 | 22 | waiting_for_instance_to_be_deleted(env, env[:machine].id) 23 | 24 | end 25 | 26 | @app.call(env) 27 | end 28 | 29 | private 30 | 31 | def waiting_for_instance_to_be_deleted(env, instance_id, retry_interval = 3) 32 | @logger.info "Waiting for the instance with id #{instance_id} to be deleted..." 33 | env[:ui].info(I18n.t('vagrant_openstack.waiting_deleted')) 34 | config = env[:machine].provider_config 35 | Timeout.timeout(config.server_delete_timeout, Errors::Timeout) do 36 | delete_ok = false 37 | until delete_ok 38 | begin 39 | @logger.debug('Waiting for instance to be DELETED') 40 | server_status = env[:openstack_client].nova.get_server_details(env, instance_id)['status'] 41 | fail Errors::ServerStatusError, server: instance_id if server_status == 'ERROR' 42 | break if server_status == 'DELETED' 43 | sleep retry_interval 44 | rescue Errors::InstanceNotFound 45 | delete_ok = true 46 | end 47 | end 48 | end 49 | end 50 | end 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/action/delete_stack.rb: -------------------------------------------------------------------------------- 1 | require 'log4r' 2 | require 'socket' 3 | require 'timeout' 4 | require 'sshkey' 5 | require 'yaml' 6 | 7 | require 'vagrant-openstack-provider/config_resolver' 8 | require 'vagrant-openstack-provider/utils' 9 | require 'vagrant-openstack-provider/action/abstract_action' 10 | require 'vagrant/util/retryable' 11 | 12 | module VagrantPlugins 13 | module Openstack 14 | module Action 15 | class DeleteStack < AbstractAction 16 | def initialize(app, _env) 17 | @app = app 18 | @logger = Log4r::Logger.new('vagrant_openstack::action::delete_stack') 19 | end 20 | 21 | def execute(env) 22 | @logger.info 'Start delete stacks action' 23 | 24 | heat = env[:openstack_client].heat 25 | 26 | list_stack_files(env).each do |stack| 27 | env[:ui].info(I18n.t('vagrant_openstack.delete_stack')) 28 | env[:ui].info(" -- Stack Name : #{stack[:name]}") 29 | env[:ui].info(" -- Stack ID : #{stack[:id]}") 30 | 31 | heat.delete_stack(env, stack[:name], stack[:id]) 32 | 33 | waiting_for_stack_to_be_deleted(env, stack[:name], stack[:id]) 34 | end 35 | 36 | # This will remove all files in the .vagrant instance directory 37 | env[:machine].id = nil 38 | 39 | @app.call(env) 40 | end 41 | 42 | private 43 | 44 | def list_stack_files(env) 45 | stack_files = [] 46 | Dir.glob("#{env[:machine].data_dir}/stack_*_id") do |stack_file| 47 | file_name = stack_file.split('/')[-1] 48 | stack_files << { 49 | name: file_name[6, (file_name.length) - 9], 50 | id: File.read("#{stack_file}") 51 | } 52 | end 53 | stack_files 54 | end 55 | 56 | def waiting_for_stack_to_be_deleted(env, stack_name, stack_id, retry_interval = 3) 57 | @logger.info "Waiting for the stack with id #{stack_id} to be deleted..." 58 | env[:ui].info(I18n.t('vagrant_openstack.waiting_for_stack_deleted')) 59 | config = env[:machine].provider_config 60 | Timeout.timeout(config.stack_delete_timeout, Errors::Timeout) do 61 | stack_status = 'DELETE_IN_PROGRESS' 62 | until stack_status == 'DELETE_COMPLETE' 63 | @logger.debug('Waiting for stack to be DELETED') 64 | stack_status = env[:openstack_client].heat.get_stack_details(env, stack_name, stack_id)['stack_status'] 65 | fail Errors::StackStatusError, stack: stack_id if stack_status == 'DELETE_FAILED' 66 | sleep retry_interval 67 | end 68 | end 69 | end 70 | end 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/action/message.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/action/abstract_action' 2 | 3 | module VagrantPlugins 4 | module Openstack 5 | module Action 6 | class Message < AbstractAction 7 | def initialize(app, _env, message) 8 | @app = app 9 | @message = message 10 | end 11 | 12 | def execute(env) 13 | env[:ui].info(@message) 14 | @app.call(env) 15 | end 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/action/provision.rb: -------------------------------------------------------------------------------- 1 | require 'log4r' 2 | 3 | require 'vagrant/action/builder' 4 | 5 | require 'vagrant-openstack-provider/action/abstract_action' 6 | require 'vagrant-openstack-provider/action/read_ssh_info' 7 | 8 | module VagrantPlugins 9 | module Openstack 10 | module Action 11 | include Vagrant::Action::Builtin 12 | 13 | class ProvisionWrapper < AbstractAction 14 | def initialize(app, env) 15 | @app = app 16 | @env = env 17 | @logger = Log4r::Logger.new('vagrant_openstack::action::provision_wrapper') 18 | end 19 | 20 | def execute(env) 21 | @logger.info 'Run provisioning' 22 | InternalProvisionWrapper.new(@app, @env).call(@env) 23 | @app.call(env) 24 | end 25 | end 26 | 27 | class InternalProvisionWrapper < Vagrant::Action::Builtin::Provision 28 | def initialize(app, env) 29 | @logger = Log4r::Logger.new('vagrant_openstack::action::internal_provision_wrapper') 30 | super app, env 31 | end 32 | 33 | def run_provisioner(env) 34 | if env[:provisioner].is_a?(Vagrant.plugin('2').manager.provisioners[:shell]) 35 | handle_shell_meta_args(env) 36 | end 37 | env[:provisioner].provision 38 | end 39 | 40 | private 41 | 42 | def handle_shell_meta_args(env) 43 | config = env[:provisioner].config 44 | args = config.args.nil? ? [] : [config.args].flatten 45 | config.args = [] 46 | @logger.info "Shell provisioner args: #{args}" 47 | args.each do |arg| 48 | if '@@ssh_ip@@'.eql? arg 49 | ssh_info = VagrantPlugins::Openstack::Action.get_ssh_info(env) 50 | @logger.info "Replace meta-arg #{arg} by value #{ssh_info[:host]}" 51 | config.args << ssh_info[:host] 52 | else 53 | config.args << arg 54 | end 55 | end 56 | end 57 | end 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/action/read_ssh_info.rb: -------------------------------------------------------------------------------- 1 | require 'log4r' 2 | 3 | require 'vagrant-openstack-provider/config_resolver' 4 | require 'vagrant-openstack-provider/utils' 5 | require 'vagrant-openstack-provider/action/abstract_action' 6 | 7 | module VagrantPlugins 8 | module Openstack 9 | module Action 10 | # This action reads the SSH info for the machine and puts it into the 11 | # `:machine_ssh_info` key in the environment. 12 | 13 | class ReadSSHInfo < AbstractAction 14 | def initialize(app, _env, resolver = ConfigResolver.new, utils = Utils.new) 15 | @app = app 16 | @logger = Log4r::Logger.new('vagrant_openstack::action::read_ssh_info') 17 | @resolver = resolver 18 | @utils = utils 19 | end 20 | 21 | def execute(env) 22 | @logger.info 'Reading SSH info' 23 | server_id = env[:machine].id.to_sym 24 | SSHInfoHolder.instance.tap do |holder| 25 | holder.synchronize do 26 | holder.ssh_info[server_id] = read_ssh_info(env) if holder.ssh_info[server_id].nil? 27 | env[:machine_ssh_info] = holder.ssh_info[server_id] 28 | end 29 | end 30 | @app.call(env) 31 | end 32 | 33 | private 34 | 35 | def read_ssh_info(env) 36 | config = env[:machine].provider_config 37 | env[:ui].warn('SSH is disabled in the provider config. The action you are attempting is likely to fail') if config.ssh_disabled 38 | hash = { 39 | host: @utils.get_ip_address(env), 40 | port: @resolver.resolve_ssh_port(env), 41 | username: @resolver.resolve_ssh_username(env) 42 | } 43 | if env[:machine].config.ssh.insert_key 44 | hash[:private_key_path] = "#{env[:machine].data_dir}/#{get_keypair_name(env)}" unless config.keypair_name || config.public_key_path 45 | end 46 | # Should work silently when https://github.com/mitchellh/vagrant/issues/4637 is fixed 47 | hash[:log_level] = 'ERROR' 48 | hash 49 | end 50 | 51 | def get_keypair_name(env) 52 | env[:openstack_client].nova.get_server_details(env, env[:machine].id)['key_name'] 53 | end 54 | end 55 | 56 | class SSHInfoHolder < Mutex 57 | include Singleton 58 | 59 | # 60 | # Keys are machine ids 61 | # 62 | attr_accessor :ssh_info 63 | 64 | def initialize 65 | @ssh_info = {} 66 | end 67 | end 68 | 69 | def self.get_ssh_info(env) 70 | SSHInfoHolder.instance.ssh_info[env[:machine].id.to_sym] 71 | end 72 | end 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/action/read_state.rb: -------------------------------------------------------------------------------- 1 | require 'log4r' 2 | 3 | require 'vagrant-openstack-provider/action/abstract_action' 4 | 5 | module VagrantPlugins 6 | module Openstack 7 | module Action 8 | # This action reads the state of the machine and puts it in the 9 | # `:machine_state_id` key in the environment. 10 | class ReadState < AbstractAction 11 | def initialize(app, _env) 12 | @app = app 13 | @logger = Log4r::Logger.new('vagrant_openstack::action::read_state') 14 | end 15 | 16 | def execute(env) 17 | env[:machine_state_id] = read_state(env) 18 | @app.call(env) 19 | end 20 | 21 | def read_state(env) 22 | machine = env[:machine] 23 | return :not_created if machine.id.nil? 24 | 25 | # Find the machine 26 | server = env[:openstack_client].nova.get_server_details(env, machine.id) 27 | if server.nil? || server['status'] == 'DELETED' 28 | # The machine can't be found 29 | @logger.info('Machine not found or deleted, assuming it got destroyed.') 30 | machine.id = nil 31 | return :not_created 32 | end 33 | 34 | if !server['OS-EXT-STS:task_state'].nil? 35 | server['OS-EXT-STS:task_state'].downcase.to_sym 36 | else 37 | server['status'].downcase.to_sym 38 | end 39 | end 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/action/resume.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/action/abstract_action' 2 | 3 | module VagrantPlugins 4 | module Openstack 5 | module Action 6 | class Resume < AbstractAction 7 | def initialize(app, _env) 8 | @app = app 9 | @logger = Log4r::Logger.new('vagrant_openstack::action::resume_server') 10 | end 11 | 12 | def execute(env) 13 | if env[:machine].id 14 | @logger.info "Resuming suspended VM #{env[:machine].id}..." 15 | env[:ui].info I18n.t('vagrant.actions.vm.resume.resuming') 16 | env[:openstack_client].nova.resume_server(env, env[:machine].id) 17 | end 18 | 19 | @app.call(env) 20 | end 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/action/snapshot_cleanup.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/action/abstract_action' 2 | 3 | module VagrantPlugins 4 | module Openstack 5 | module Action 6 | class SnapshotCleanup < AbstractAction 7 | def initialize(app, _env) 8 | @app = app 9 | end 10 | 11 | def call(env) 12 | nova = env[:openstack_client].nova 13 | machine_snapshots = nova.list_snapshots(env, env[:machine].id) 14 | 15 | snapshots_to_clean = machine_snapshots.select do |s| 16 | s.metadata.key?('vagrant_snapshot') 17 | end 18 | 19 | @app.call env 20 | 21 | unless snapshots_to_clean.empty? 22 | env[:ui].info("Deleting Vagrant snapshots: #{snapshots_to_clean.map(&:name)}") 23 | end 24 | 25 | snapshots_to_clean.each do |s| 26 | nova.delete_snapshot(env, s.id) 27 | end 28 | end 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/action/snapshot_delete.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/action/abstract_action' 2 | 3 | module VagrantPlugins 4 | module Openstack 5 | module Action 6 | class SnapshotDelete < AbstractAction 7 | def initialize(app, _env) 8 | @app = app 9 | end 10 | 11 | def call(env) 12 | nova = env[:openstack_client].nova 13 | machine_snapshots = nova.list_snapshots(env, env[:machine].id) 14 | 15 | snapshot = machine_snapshots.find { |s| s.name == env[:snapshot_name] } 16 | 17 | unless snapshot.nil? 18 | env[:ui].info(I18n.t('vagrant.actions.vm.snapshot.deleting', 19 | name: snapshot.name)) 20 | 21 | nova.delete_snapshot(env, snapshot.id) 22 | 23 | env[:ui].info(I18n.t('vagrant.actions.vm.snapshot.deleted', 24 | name: snapshot.name)) 25 | end 26 | 27 | @app.call env 28 | end 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/action/snapshot_list.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/action/abstract_action' 2 | 3 | module VagrantPlugins 4 | module Openstack 5 | module Action 6 | class SnapshotList < AbstractAction 7 | def initialize(app, _env) 8 | @app = app 9 | end 10 | 11 | def call(env) 12 | nova = env[:openstack_client].nova 13 | machine_snapshots = nova.list_snapshots(env, env[:machine].id) 14 | 15 | env[:machine_snapshot_list] = machine_snapshots.map(&:name) 16 | 17 | @app.call env 18 | end 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/action/snapshot_restore.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/action/abstract_action' 2 | 3 | module VagrantPlugins 4 | module Openstack 5 | module Action 6 | class SnapshotRestore < AbstractAction 7 | def initialize(app, _env) 8 | @app = app 9 | end 10 | 11 | def call(env) 12 | nova = env[:openstack_client].nova 13 | machine_snapshots = nova.list_snapshots(env, env[:machine].id) 14 | 15 | snapshot = machine_snapshots.find { |s| s.name == env[:snapshot_name] } 16 | 17 | unless snapshot.nil? 18 | env[:ui].info(I18n.t('vagrant.actions.vm.snapshot.restoring', 19 | name: snapshot.name)) 20 | 21 | nova.restore_snapshot(env, env[:machine].id, snapshot.id) 22 | end 23 | 24 | @app.call env 25 | end 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/action/snapshot_save.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/action/abstract_action' 2 | 3 | module VagrantPlugins 4 | module Openstack 5 | module Action 6 | class SnapshotSave < AbstractAction 7 | def initialize(app, _env, retry_interval = 3) 8 | @app = app 9 | @retry_interval = retry_interval 10 | end 11 | 12 | def call(env) 13 | nova = env[:openstack_client].nova 14 | config = env[:machine].provider_config 15 | 16 | env[:ui].info(I18n.t('vagrant.actions.vm.snapshot.saving', 17 | name: env[:snapshot_name])) 18 | 19 | nova.create_snapshot( 20 | env, 21 | env[:machine].id, env[:snapshot_name]) 22 | 23 | image = nova.list_snapshots(env, env[:machine].id).find { |i| i.name == env[:snapshot_name] } 24 | 25 | timeout(config.server_create_timeout, Errors::Timeout) do 26 | loop do 27 | image_status = nova.get_image_details(env, image.id) 28 | 29 | break if image_status['status'] == 'ACTIVE' 30 | 31 | unless image_status['progress'].nil? 32 | env[:ui].clear_line 33 | env[:ui].report_progress(image_status['progress'], 100, false) 34 | end 35 | 36 | sleep @retry_interval 37 | end 38 | end 39 | 40 | # Clear progress output. 41 | env[:ui].clear_line 42 | 43 | env[:ui].success(I18n.t('vagrant.actions.vm.snapshot.saved', 44 | name: env[:snapshot_name])) 45 | 46 | @app.call env 47 | end 48 | end 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/action/start_server.rb: -------------------------------------------------------------------------------- 1 | require 'log4r' 2 | 3 | require 'vagrant-openstack-provider/action/abstract_action' 4 | 5 | module VagrantPlugins 6 | module Openstack 7 | module Action 8 | class StartServer < AbstractAction 9 | def initialize(app, _env) 10 | @app = app 11 | @logger = Log4r::Logger.new('vagrant_openstack::action::start_server') 12 | end 13 | 14 | def execute(env) 15 | if env[:machine].id 16 | env[:ui].info(I18n.t('vagrant_openstack.starting_server')) 17 | env[:openstack_client].nova.start_server(env, env[:machine].id) 18 | end 19 | @app.call(env) 20 | end 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/action/stop_server.rb: -------------------------------------------------------------------------------- 1 | require 'log4r' 2 | 3 | require 'vagrant-openstack-provider/action/abstract_action' 4 | 5 | module VagrantPlugins 6 | module Openstack 7 | module Action 8 | class StopServer < AbstractAction 9 | def initialize(app, _env) 10 | @app = app 11 | @logger = Log4r::Logger.new('vagrant_openstack::action::stop_server') 12 | end 13 | 14 | def execute(env) 15 | if env[:machine].id 16 | @logger.info "Stopping server #{env[:machine].id}..." 17 | env[:ui].info(I18n.t('vagrant_openstack.stopping_server')) 18 | env[:openstack_client].nova.stop_server(env, env[:machine].id) 19 | end 20 | @app.call(env) 21 | end 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/action/suspend.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/action/abstract_action' 2 | 3 | module VagrantPlugins 4 | module Openstack 5 | module Action 6 | class Suspend < AbstractAction 7 | def initialize(app, _env) 8 | @app = app 9 | @logger = Log4r::Logger.new('vagrant_openstack::action::suspend_server') 10 | end 11 | 12 | def execute(env) 13 | if env[:machine].id 14 | @logger.info "Saving VM #{env[:machine].id} state and suspending execution..." 15 | env[:ui].info I18n.t('vagrant.actions.vm.suspend.suspending') 16 | env[:openstack_client].nova.suspend_server(env, env[:machine].id) 17 | end 18 | 19 | @app.call(env) 20 | end 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/action/sync_folders.rb: -------------------------------------------------------------------------------- 1 | require 'log4r' 2 | require 'rbconfig' 3 | require 'vagrant/util/subprocess' 4 | 5 | require 'vagrant-openstack-provider/action/abstract_action' 6 | 7 | module VagrantPlugins 8 | module Openstack 9 | module Action 10 | class SyncFolders < AbstractAction 11 | def initialize(app, _env) 12 | @app = app 13 | end 14 | 15 | def execute(env) 16 | sync_method = env[:machine].provider_config.sync_method 17 | if sync_method == 'none' 18 | NoSyncFolders.new(@app, env).call(env) 19 | elsif sync_method == 'rsync' 20 | RsyncFolders.new(@app, env).call(env) 21 | else 22 | fail Errors::SyncMethodError, sync_method_value: sync_method 23 | end 24 | end 25 | end 26 | 27 | class NoSyncFolders 28 | def initialize(app, _env) 29 | @app = app 30 | end 31 | 32 | def call(env) 33 | @app.call(env) 34 | env[:ui].info(I18n.t('vagrant_openstack.disabled_sync_folders')) 35 | end 36 | end 37 | 38 | # This middleware uses `rsync` to sync the folders over to the 39 | # remote instance. 40 | class RsyncFolders 41 | def initialize(app, _env) 42 | @app = app 43 | @logger = Log4r::Logger.new('vagrant_openstack::action::sync_folders') 44 | @host_os = RbConfig::CONFIG['host_os'] 45 | end 46 | 47 | def call(env) 48 | @app.call(env) 49 | 50 | if env[:machine].provider_config.ssh_disabled 51 | env[:ui].info(I18n.t('vagrant_openstack.ssh_disabled_sync_folders')) 52 | return 53 | end 54 | 55 | ssh_info = env[:machine].ssh_info 56 | 57 | config = env[:machine].provider_config 58 | rsync_includes = config.rsync_includes.to_a 59 | 60 | env[:machine].config.vm.synced_folders.each do |_, data| 61 | hostpath = File.expand_path(data[:hostpath], env[:root_path]) 62 | guestpath = data[:guestpath] 63 | 64 | # Make sure there is a trailing slash on the host path to 65 | # avoid creating an additional directory with rsync 66 | hostpath = "#{hostpath}/" if hostpath !~ /\/$/ 67 | 68 | # If on Windows, modify the path to work with cygwin rsync 69 | if @host_os =~ /mswin|mingw|cygwin/ 70 | hostpath = add_cygdrive_prefix_to_path(hostpath) 71 | end 72 | 73 | env[:ui].info(I18n.t('vagrant_openstack.rsync_folder', hostpath: hostpath, guestpath: guestpath)) 74 | 75 | # Create the guest path 76 | env[:machine].communicate.sudo("mkdir -p '#{guestpath}'") 77 | env[:machine].communicate.sudo("chown -R #{ssh_info[:username]} '#{guestpath}'") 78 | 79 | # Generate rsync include commands 80 | includes = rsync_includes.each_with_object([]) do |incl, incls| 81 | incls << '--include' 82 | incls << incl 83 | end 84 | 85 | # Create ssh params for rsync 86 | # we need them as one string because rsync -e expects one string 87 | ssh_params = [ 88 | "ssh -p #{ssh_info[:port]}", 89 | '-o StrictHostKeyChecking=no', 90 | '-o UserKnownHostsFile=/dev/null', 91 | '-o IdentitiesOnly=yes', 92 | "#{ssh_key_options(ssh_info)}"].join(' ') 93 | 94 | # Rsync over to the guest path using the SSH info. add 95 | # .hg/ and .git/ to exclude list as that isn't covered in 96 | # --cvs-exclude 97 | command = [ 98 | 'rsync', '--verbose', '--archive', '-z', 99 | '--cvs-exclude', 100 | '--exclude', '.hg/', 101 | '--exclude', '.git/', 102 | '--chmod', 'ugo=rwX', 103 | *includes, 104 | '-e', ssh_params, 105 | hostpath, 106 | "#{ssh_info[:username]}@#{ssh_info[:host]}:#{guestpath}"] 107 | command.compact! 108 | 109 | # during rsync, ignore files specified in list of files containing exclude patterns 110 | # ex: rsync_ignore_files = ['.hgignore', '.gitignore'] 111 | ignore_files = [] 112 | ignore_files = env[:machine].provider_config.rsync_ignore_files unless env[:machine].provider_config.rsync_ignore_files.nil? 113 | ignore_files.each do |ignore_file| 114 | abs_ignore_file = env[:root_path].to_s + '/' + ignore_file 115 | command += ['--exclude-from', abs_ignore_file] if File.exist?(abs_ignore_file) 116 | end 117 | r = Vagrant::Util::Subprocess.execute(*command) 118 | next if r.exit_code == 0 119 | fail Errors::RsyncError, guestpath: guestpath, hostpath: hostpath, stderr: r.stderr 120 | end 121 | end 122 | 123 | private 124 | 125 | def ssh_key_options(ssh_info) 126 | # Ensure that `private_key_path` is an Array (for Vagrant < 1.4) 127 | Array(ssh_info[:private_key_path]).map { |path| "-i '#{path}' " }.join 128 | end 129 | 130 | def add_cygdrive_prefix_to_path(hostpath) 131 | hostpath.downcase.sub(/^([a-z]):\//) do 132 | "/cygdrive/#{Regexp.last_match[1]}/" 133 | end 134 | end 135 | end 136 | end 137 | end 138 | end 139 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/action/wait_active.rb: -------------------------------------------------------------------------------- 1 | require 'log4r' 2 | require 'timeout' 3 | 4 | require 'vagrant-openstack-provider/action/abstract_action' 5 | 6 | module VagrantPlugins 7 | module Openstack 8 | module Action 9 | class WaitForServerToBeActive < AbstractAction 10 | def initialize(app, _env, retry_interval = 3) 11 | @app = app 12 | @logger = Log4r::Logger.new('vagrant_openstack::action::start_server') 13 | @retry_interval = retry_interval 14 | end 15 | 16 | def execute(env) 17 | if env[:machine].id 18 | env[:ui].info(I18n.t('vagrant_openstack.waiting_start')) 19 | client = env[:openstack_client].nova 20 | config = env[:machine].provider_config 21 | Timeout.timeout(config.server_active_timeout, Errors::Timeout) do 22 | while client.get_server_details(env, env[:machine].id)['status'] != 'ACTIVE' 23 | sleep @retry_interval 24 | @logger.info('Waiting for server to be active') 25 | end 26 | end 27 | end 28 | @app.call(env) 29 | end 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/action/wait_stop.rb: -------------------------------------------------------------------------------- 1 | require 'log4r' 2 | require 'timeout' 3 | 4 | require 'vagrant-openstack-provider/action/abstract_action' 5 | 6 | module VagrantPlugins 7 | module Openstack 8 | module Action 9 | class WaitForServerToStop < AbstractAction 10 | def initialize(app, _env, retry_interval = 3) 11 | @app = app 12 | @logger = Log4r::Logger.new('vagrant_openstack::action::stop_server') 13 | @retry_interval = retry_interval 14 | end 15 | 16 | def execute(env) 17 | if env[:machine].id 18 | env[:ui].info(I18n.t('vagrant_openstack.waiting_stop')) 19 | client = env[:openstack_client].nova 20 | config = env[:machine].provider_config 21 | Timeout.timeout(config.server_stop_timeout, Errors::Timeout) do 22 | while client.get_server_details(env, env[:machine].id)['status'] != 'SHUTOFF' 23 | sleep @retry_interval 24 | @logger.info('Waiting for server to stop') 25 | end 26 | end 27 | end 28 | @app.call(env) 29 | end 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/cap/snapshot_list.rb: -------------------------------------------------------------------------------- 1 | module VagrantPlugins 2 | module Openstack 3 | module Cap 4 | module SnapshotList 5 | # Returns a list of the snapshots that are taken on this machine. 6 | # 7 | # @return [Array] 8 | def self.snapshot_list(machine) 9 | env = machine.action(:snapshot_list, lock: false) 10 | env[:machine_snapshot_list] 11 | end 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/catalog/openstack_catalog.rb: -------------------------------------------------------------------------------- 1 | module VagrantPlugins 2 | module Openstack 3 | module Catalog 4 | class OpenstackCatalog 5 | def initialize 6 | @logger = Log4r::Logger.new('vagrant_openstack::action::openstack_reader') 7 | end 8 | 9 | def read(env, catalog) 10 | config = env[:machine].provider_config 11 | client = env[:openstack_client] 12 | endpoints = client.session.endpoints 13 | @logger.info(I18n.t('vagrant_openstack.client.looking_for_available_endpoints')) 14 | @logger.info("Selecting endpoints matching region '#{config.region}'") unless config.region.nil? 15 | 16 | catalog.each do |service| 17 | se = service['endpoints'] 18 | if config.identity_api_version == '2' 19 | get_endpoints_2(env, se, service, config, endpoints) 20 | elsif config.identity_api_version == '3' 21 | get_interfaces_3(se, service, config, endpoints) 22 | end 23 | end 24 | 25 | endpoints[:network] = choose_api_version('Neutron', 'openstack_network_url', 'v2') do 26 | client.neutron.get_api_version_list(env, :network) 27 | end if config.openstack_network_url.nil? && !endpoints[:network].nil? 28 | 29 | endpoints[:image] = choose_api_version('Glance', 'openstack_image_url', nil, false) do 30 | client.glance.get_api_version_list(env) 31 | end if config.openstack_image_url.nil? && !endpoints[:image].nil? 32 | end 33 | 34 | private 35 | 36 | def get_endpoints_2(env, se, service, config, endpoints) 37 | endpoint_type = config.endpoint_type 38 | if config.region.nil? 39 | if se.size > 1 40 | env[:ui].warn I18n.t('vagrant_openstack.client.multiple_endpoint', size: se.size, type: service['type']) 41 | env[:ui].warn " => #{service['endpoints'][0][endpoint_type]}" 42 | end 43 | url = se[0][endpoint_type].strip 44 | else 45 | se.each do |endpoint| 46 | url = endpoint[endpoint_type].strip if endpoint['region'].eql? config.region 47 | end 48 | end 49 | endpoints[service['type'].to_sym] = url unless url.nil? || url.empty? 50 | end 51 | 52 | def get_interfaces_3(se, service, config, endpoints) 53 | url = nil 54 | se.each do |endpoint| 55 | next if endpoint['interface'] != config.interface_type 56 | if config.region.nil? 57 | url = endpoint['url'] 58 | break 59 | elsif endpoint['region'] == config.region 60 | url = endpoint['url'] 61 | break 62 | end 63 | end 64 | endpoints[service['type'].to_sym] = url unless url.nil? || url.empty? 65 | end 66 | 67 | def choose_api_version(service_name, url_property, version_prefix = nil, fail_if_not_found = true) 68 | versions = yield 69 | 70 | return versions.first['links'].first['href'] if version_prefix.nil? 71 | 72 | if versions.size == 1 73 | return versions.first['links'].first['href'] if versions.first['id'].start_with?(version_prefix) 74 | fail Errors::NoMatchingApiVersion, api_name: service_name, url_property: url_property, version_list: version_list if fail_if_not_found 75 | end 76 | 77 | version_list = '' 78 | versions.each do |version| 79 | return version['links'].first['href'] if version['id'].start_with?(version_prefix) 80 | links = version['links'].map { |l| l['href'] } 81 | version_list << "#{version['id'].ljust(6)} #{version['status'].ljust(10)} #{links}\n" 82 | end 83 | 84 | fail Errors::NoMatchingApiVersion, api_name: service_name, url_property: url_property, version_list: version_list if fail_if_not_found 85 | nil 86 | end 87 | end 88 | end 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/client/cinder.rb: -------------------------------------------------------------------------------- 1 | require 'log4r' 2 | require 'json' 3 | 4 | require 'vagrant-openstack-provider/client/http_utils' 5 | require 'vagrant-openstack-provider/client/domain' 6 | 7 | module VagrantPlugins 8 | module Openstack 9 | class CinderClient 10 | include Singleton 11 | include VagrantPlugins::Openstack::HttpUtils 12 | include VagrantPlugins::Openstack::Domain 13 | 14 | def initialize 15 | @logger = Log4r::Logger.new('vagrant_openstack::cinder') 16 | @session = VagrantPlugins::Openstack.session 17 | end 18 | 19 | def get_all_volumes(env) 20 | endpoint = @session.endpoints[:volumev2] || @session.endpoints[:volume] 21 | volumes_json = get(env, "#{endpoint}/volumes/detail") 22 | JSON.parse(volumes_json)['volumes'].map do |volume| 23 | name = volume['display_name'] 24 | name = volume['name'] if name.nil? # To be compatible with cinder api v1 and v2 25 | case volume['attachments'].size 26 | when 0 27 | @logger.debug "No attachment found for volume #{volume['id']}" 28 | else 29 | attachment = volume['attachments'][0] 30 | server_id = attachment['server_id'] 31 | device = attachment['device'] 32 | @logger.warn "Found #{attachment.size} attachments for volume #{volume['id']} : " if attachment.size > 1 33 | @logger.debug "Attachment found for volume #{volume['id']} : #{attachment.to_json}" 34 | end 35 | Volume.new(volume['id'], name, volume['size'], volume['status'], volume['bootable'], server_id, device) 36 | end 37 | end 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/client/domain.rb: -------------------------------------------------------------------------------- 1 | require 'log4r' 2 | require 'json' 3 | 4 | module VagrantPlugins 5 | module Openstack 6 | module Domain 7 | class Item 8 | attr_accessor :id, :name 9 | def initialize(id, name) 10 | @id = id 11 | @name = name 12 | end 13 | 14 | def ==(other) 15 | other.class == self.class && other.state == state 16 | end 17 | 18 | def state 19 | [@id, @name] 20 | end 21 | end 22 | 23 | class Image < Item 24 | attr_accessor :visibility 25 | attr_accessor :size 26 | attr_accessor :min_ram 27 | attr_accessor :min_disk 28 | attr_accessor :metadata 29 | 30 | # rubocop:disable Metrics/ParameterLists 31 | def initialize(id, name, visibility = nil, size = nil, min_ram = nil, min_disk = nil, metadata = {}) 32 | @visibility = visibility 33 | @size = size 34 | @min_ram = min_ram 35 | @min_disk = min_disk 36 | @metadata = metadata 37 | super(id, name) 38 | end 39 | # rubocop:enable Metrics/ParameterLists 40 | 41 | protected 42 | 43 | def state 44 | [@id, @name, @visibility, @size, @min_ram, @min_disk, @metadata] 45 | end 46 | end 47 | 48 | class Flavor < Item 49 | # 50 | # The number of vCPU 51 | # 52 | attr_accessor :vcpus 53 | 54 | # 55 | # The amount of RAM in Megaoctet 56 | # 57 | attr_accessor :ram 58 | 59 | # 60 | # The size of root disk in Gigaoctet 61 | # 62 | attr_accessor :disk 63 | 64 | def initialize(id, name, vcpus, ram, disk) 65 | @vcpus = vcpus 66 | @ram = ram 67 | @disk = disk 68 | super(id, name) 69 | end 70 | 71 | protected 72 | 73 | def state 74 | [@id, @name, @vcpus, @ram, @disk] 75 | end 76 | end 77 | 78 | class FloatingIP 79 | attr_accessor :ip, :pool, :instance_id 80 | def initialize(ip, pool, instance_id) 81 | @ip = ip 82 | @pool = pool 83 | @instance_id = instance_id 84 | end 85 | end 86 | 87 | class Volume < Item 88 | # 89 | # Size in Gigaoctet 90 | # 91 | attr_accessor :size 92 | 93 | # 94 | # Status (e.g. 'Available', 'In-use') 95 | # 96 | attr_accessor :status 97 | 98 | # 99 | # Whether volume is bootable or not 100 | # 101 | attr_accessor :bootable 102 | 103 | # 104 | # instance id volume is attached to 105 | # 106 | attr_accessor :instance_id 107 | 108 | # 109 | # device (e.g. /dev/sdb) if attached 110 | # 111 | attr_accessor :device 112 | 113 | # rubocop:disable Metrics/ParameterLists 114 | def initialize(id, name, size, status, bootable, instance_id, device) 115 | @size = size 116 | @status = status 117 | @bootable = bootable 118 | @instance_id = instance_id 119 | @device = device 120 | super(id, name) 121 | end 122 | # rubocop:enable Metrics/ParameterLists 123 | 124 | def to_s 125 | { 126 | id: @id, 127 | name: @name, 128 | size: @size, 129 | status: @status, 130 | bootable: @bootable, 131 | instance_id: @instance_id, 132 | device: @device 133 | }.to_json 134 | end 135 | 136 | protected 137 | 138 | def state 139 | [@id, @name, @size, @status, @bootable, @instance_id, @device] 140 | end 141 | end 142 | 143 | class Subnet < Item 144 | attr_accessor :cidr 145 | attr_accessor :enable_dhcp 146 | attr_accessor :network_id 147 | 148 | def initialize(id, name, cidr, enable_dhcp, network_id) 149 | @cidr = cidr 150 | @enable_dhcp = enable_dhcp 151 | @network_id = network_id 152 | super(id, name) 153 | end 154 | 155 | protected 156 | 157 | def state 158 | [@id, @name, @cidr, @enable_dhcp, @network_id] 159 | end 160 | end 161 | end 162 | end 163 | end 164 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/client/glance.rb: -------------------------------------------------------------------------------- 1 | require 'log4r' 2 | require 'json' 3 | 4 | require 'vagrant-openstack-provider/client/http_utils' 5 | require 'vagrant-openstack-provider/client/rest_utils' 6 | require 'vagrant-openstack-provider/client/domain' 7 | 8 | module VagrantPlugins 9 | module Openstack 10 | class GlanceClient 11 | include Singleton 12 | include VagrantPlugins::Openstack::HttpUtils 13 | include VagrantPlugins::Openstack::Domain 14 | 15 | def initialize 16 | @logger = Log4r::Logger.new('vagrant_openstack::glance') 17 | @session = VagrantPlugins::Openstack.session 18 | end 19 | 20 | def get_api_version_list(env) 21 | json = RestUtils.get(env, @session.endpoints[:image], 22 | 'X-Auth-Token' => @session.token, 23 | :accept => :json) do |response| 24 | log_response(response) 25 | case response.code 26 | when 200, 300 27 | response 28 | when 401 29 | fail Errors::AuthenticationFailed 30 | else 31 | fail Errors::VagrantOpenstackError, message: response.to_s 32 | end 33 | end 34 | JSON.parse(json)['versions'] 35 | end 36 | 37 | # Endpoint /images exists on both v1 and v2 API 38 | # The attribute 'visibility' is used to detect 39 | # if the call has been made on v1 or v2 40 | # 41 | # In case of v2 we have all the needed information, 42 | # but in case of v1 we don't and we have to call 43 | # /images/detail to get full details 44 | # 45 | def get_all_images(env) 46 | images_json = get(env, "#{@session.endpoints[:image]}/images") 47 | images = JSON.parse(images_json)['images'] 48 | 49 | return images if images.empty? 50 | 51 | is_v1 = false 52 | unless images[0].key? 'visibility' 53 | is_v1 = true 54 | images_json = get(env, "#{@session.endpoints[:image]}/images/detail") 55 | images = JSON.parse(images_json)['images'] 56 | end 57 | 58 | images.map do |i| 59 | i['visibility'] = i['is_public'] ? 'public' : 'private' if is_v1 60 | Image.new(i['id'], i['name'], i['visibility'], i['size'], i['min_ram'], i['min_disk']) 61 | end 62 | end 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/client/heat.rb: -------------------------------------------------------------------------------- 1 | require 'log4r' 2 | require 'json' 3 | 4 | require 'vagrant-openstack-provider/client/http_utils' 5 | require 'vagrant-openstack-provider/client/domain' 6 | 7 | module VagrantPlugins 8 | module Openstack 9 | class HeatClient 10 | include Singleton 11 | include VagrantPlugins::Openstack::HttpUtils 12 | include VagrantPlugins::Openstack::Domain 13 | 14 | def initialize 15 | @logger = Log4r::Logger.new('vagrant_openstack::glance') 16 | @session = VagrantPlugins::Openstack.session 17 | end 18 | 19 | def create_stack(env, options) 20 | stack = {}.tap do |s| 21 | s['stack_name'] = options[:name] if options[:name] 22 | s['template'] = options[:template] 23 | end 24 | stack_res = post(env, "#{@session.endpoints[:orchestration]}/stacks", stack.to_json) 25 | JSON.parse(stack_res)['stack']['id'] 26 | end 27 | 28 | def get_stack_details(env, stack_name, stack_id) 29 | stack_exists do 30 | server_details = get(env, "#{@session.endpoints[:orchestration]}/stacks/#{stack_name}/#{stack_id}") 31 | JSON.parse(server_details)['stack'] 32 | end 33 | end 34 | 35 | def delete_stack(env, stack_name, stack_id) 36 | stack_exists do 37 | delete(env, "#{@session.endpoints[:orchestration]}/stacks/#{stack_name}/#{stack_id}") 38 | end 39 | end 40 | 41 | def stack_exists 42 | return yield 43 | rescue Errors::VagrantOpenstackError => e 44 | raise Errors::StackNotFound if e.extra_data[:code] == 404 45 | raise e 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/client/http_utils.rb: -------------------------------------------------------------------------------- 1 | require 'log4r' 2 | require 'json' 3 | 4 | require 'vagrant-openstack-provider/client/keystone' 5 | require 'vagrant-openstack-provider/client/request_logger' 6 | require 'vagrant-openstack-provider/client/rest_utils' 7 | 8 | module VagrantPlugins 9 | module Openstack 10 | module HttpUtils 11 | include VagrantPlugins::Openstack::HttpUtils::RequestLogger 12 | 13 | def get(env, url, headers = {}) 14 | calling_method = caller[0][/`.*'/][1..-2] 15 | @logger.debug("#{calling_method} - start") 16 | 17 | headers.merge!('X-Auth-Token' => @session.token, :accept => :json) 18 | 19 | log_request(:GET, url, headers) 20 | 21 | authenticated(env) do 22 | RestUtils.get(env, url, headers) { |res| handle_response(res) }.tap do 23 | @logger.debug("#{calling_method} - end") 24 | end 25 | end 26 | end 27 | 28 | def post(env, url, body = nil, headers = {}) 29 | calling_method = caller[0][/`.*'/][1..-2] 30 | @logger.debug("#{calling_method} - start") 31 | 32 | headers.merge!('X-Auth-Token' => @session.token, :accept => :json, :content_type => :json) 33 | 34 | log_request(:POST, url, body, headers) 35 | 36 | authenticated(env) do 37 | RestUtils.post(env, url, body, headers) { |res| handle_response(res) }.tap do 38 | @logger.debug("#{calling_method} - end") 39 | end 40 | end 41 | end 42 | 43 | def delete(env, url, headers = {}) 44 | calling_method = caller[0][/`.*'/][1..-2] 45 | @logger.debug("#{calling_method} - start") 46 | 47 | headers.merge!('X-Auth-Token' => @session.token, :accept => :json, :content_type => :json) 48 | 49 | log_request(:DELETE, url, headers) 50 | 51 | authenticated(env) do 52 | RestUtils.delete(env, url, headers) { |res| handle_response(res) }.tap do 53 | @logger.debug("#{calling_method} - end") 54 | end 55 | end 56 | end 57 | 58 | def get_api_version_list(env, service_type) 59 | url = @session.endpoints[service_type] 60 | headers = { 'X-Auth-Token' => @session.token, :accept => :json } 61 | log_request(:GET, url, headers) 62 | 63 | json = RestUtils.get(env, url, headers) do |response| 64 | log_response(response) 65 | case response.code 66 | when 200, 300 67 | response 68 | when 401 69 | fail Errors::AuthenticationFailed 70 | else 71 | fail Errors::VagrantOpenstackError, message: response.to_s 72 | end 73 | end 74 | JSON.parse(json)['versions'] 75 | end 76 | 77 | private 78 | 79 | ERRORS = 80 | { 81 | '400' => 'badRequest', 82 | '404' => 'itemNotFound', 83 | '409' => 'conflictingRequest' 84 | } 85 | 86 | def handle_response(response) 87 | log_response(response) 88 | case response.code 89 | when 200, 201, 202, 204 90 | response 91 | when 401 92 | fail Errors::AuthenticationRequired 93 | when 400, 404, 409 94 | message = JSON.parse(response.to_s)[ERRORS[response.code.to_s]]['message'] 95 | fail Errors::VagrantOpenstackError, message: message, code: response.code 96 | else 97 | fail Errors::VagrantOpenstackError, message: response.to_s, code: response.code 98 | end 99 | end 100 | 101 | def authenticated(env) 102 | nb_retry = 0 103 | begin 104 | return yield 105 | rescue Errors::AuthenticationRequired => e 106 | nb_retry += 1 107 | env[:ui].warn(e) 108 | env[:ui].warn(I18n.t('vagrant_openstack.trying_authentication')) 109 | env[:openstack_client].keystone.authenticate(env) 110 | retry if nb_retry < 3 111 | raise e 112 | end 113 | end 114 | end 115 | end 116 | end 117 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/client/keystone.rb: -------------------------------------------------------------------------------- 1 | require 'log4r' 2 | require 'json' 3 | 4 | require 'vagrant-openstack-provider/client/request_logger' 5 | 6 | module VagrantPlugins 7 | module Openstack 8 | class KeystoneClient 9 | include Singleton 10 | include VagrantPlugins::Openstack::HttpUtils::RequestLogger 11 | 12 | def initialize 13 | @logger = Log4r::Logger.new('vagrant_openstack::keystone') 14 | @session = VagrantPlugins::Openstack.session 15 | end 16 | 17 | def authenticate(env) 18 | @logger.info('Authenticating on Keystone') 19 | config = env[:machine].provider_config 20 | @logger.info(I18n.t('vagrant_openstack.client.authentication', project: config.tenant_name, user: config.username)) 21 | 22 | if config.identity_api_version == '2' 23 | post_body = get_body_2 config 24 | auth_url = get_auth_url_2 env 25 | elsif config.identity_api_version == '3' 26 | post_body = get_body_3 config 27 | auth_url = get_auth_url_3 env 28 | end 29 | 30 | headers = { 31 | content_type: :json, 32 | accept: :json 33 | } 34 | 35 | log_request(:POST, auth_url, post_body.to_json, headers) 36 | 37 | if config.identity_api_version == '2' 38 | post_body[:auth][:passwordCredentials][:password] = config.password 39 | elsif config.identity_api_version == '3' 40 | post_body[:auth][:identity][:password][:user][:password] = config.password 41 | end 42 | 43 | authentication = RestUtils.post(env, auth_url, post_body.to_json, headers) do |response| 44 | log_response(response) 45 | case response.code 46 | when 200 47 | response 48 | when 201 49 | response 50 | when 401 51 | fail Errors::AuthenticationFailed 52 | when 404 53 | fail Errors::BadAuthenticationEndpoint 54 | else 55 | fail Errors::VagrantOpenstackError, message: response.to_s 56 | end 57 | end 58 | 59 | if config.identity_api_version == '2' 60 | access = JSON.parse(authentication)['access'] 61 | response_token = access['token'] 62 | @session.token = response_token['id'] 63 | @session.project_id = response_token['tenant']['id'] 64 | return access['serviceCatalog'] 65 | elsif config.identity_api_version == '3' 66 | body = JSON.parse(authentication) 67 | @session.token = authentication.headers[:x_subject_token] 68 | @session.project_id = body['token']['project']['id'] 69 | return body['token']['catalog'] 70 | end 71 | end 72 | 73 | private 74 | 75 | def get_body_2(config) 76 | { 77 | auth: 78 | { 79 | tenantName: config.tenant_name, 80 | passwordCredentials: 81 | { 82 | username: config.username, 83 | password: '****' 84 | } 85 | } 86 | } 87 | end 88 | 89 | def get_body_3(config) 90 | { 91 | auth: 92 | { 93 | identity: { 94 | methods: ['password'], 95 | password: { 96 | user: { 97 | name: config.username, 98 | domain: { 99 | name: config.user_domain_name 100 | }, 101 | password: '****' 102 | } 103 | } 104 | }, 105 | scope: { 106 | project: { 107 | name: config.project_name, 108 | domain: { name: config.project_domain_name } 109 | } 110 | } 111 | } 112 | } 113 | end 114 | 115 | def get_auth_url_3(env) 116 | url = env[:machine].provider_config.openstack_auth_url 117 | return url if url.match(%r{/tokens/*$}) 118 | "#{url}/auth/tokens" 119 | end 120 | 121 | def get_auth_url_2(env) 122 | url = env[:machine].provider_config.openstack_auth_url 123 | return url if url.match(%r{/tokens/*$}) 124 | "#{url}/tokens" 125 | end 126 | end 127 | end 128 | end 129 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/client/neutron.rb: -------------------------------------------------------------------------------- 1 | require 'log4r' 2 | require 'json' 3 | 4 | require 'vagrant-openstack-provider/client/http_utils' 5 | require 'vagrant-openstack-provider/client/domain' 6 | 7 | module VagrantPlugins 8 | module Openstack 9 | class NeutronClient 10 | include Singleton 11 | include VagrantPlugins::Openstack::HttpUtils 12 | include VagrantPlugins::Openstack::Domain 13 | 14 | def initialize 15 | @logger = Log4r::Logger.new('vagrant_openstack::neutron') 16 | @session = VagrantPlugins::Openstack.session 17 | end 18 | 19 | def get_private_networks(env) 20 | get_networks(env, false) 21 | end 22 | 23 | def get_all_networks(env) 24 | get_networks(env, true) 25 | end 26 | 27 | def get_subnets(env) 28 | subnets_json = get(env, "#{@session.endpoints[:network]}/subnets") 29 | subnets = [] 30 | JSON.parse(subnets_json)['subnets'].each do |n| 31 | subnets << Subnet.new(n['id'], n['name'], n['cidr'], n['enable_dhcp'], n['network_id']) 32 | end 33 | subnets 34 | end 35 | 36 | private 37 | 38 | def get_networks(env, all) 39 | networks_json = get(env, "#{@session.endpoints[:network]}/networks") 40 | networks = [] 41 | JSON.parse(networks_json)['networks'].each do |n| 42 | networks << Item.new(n['id'], n['name']) if all || n['tenant_id'].eql?(@session.project_id) 43 | end 44 | networks 45 | end 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/client/openstack.rb: -------------------------------------------------------------------------------- 1 | require 'log4r' 2 | require 'json' 3 | 4 | require 'vagrant-openstack-provider/client/heat' 5 | require 'vagrant-openstack-provider/client/keystone' 6 | require 'vagrant-openstack-provider/client/nova' 7 | require 'vagrant-openstack-provider/client/neutron' 8 | require 'vagrant-openstack-provider/client/cinder' 9 | require 'vagrant-openstack-provider/client/glance' 10 | 11 | module VagrantPlugins 12 | module Openstack 13 | class Session 14 | include Singleton 15 | 16 | attr_accessor :token 17 | attr_accessor :project_id 18 | attr_accessor :endpoints 19 | 20 | def initialize 21 | @token = nil 22 | @project_id = nil 23 | @endpoints = {} 24 | end 25 | 26 | def reset 27 | initialize 28 | end 29 | end 30 | 31 | def self.session 32 | Session.instance 33 | end 34 | 35 | def self.keystone 36 | Openstack::KeystoneClient.instance 37 | end 38 | 39 | def self.nova 40 | Openstack::NovaClient.instance 41 | end 42 | 43 | def self.heat 44 | Openstack::HeatClient.instance 45 | end 46 | 47 | def self.neutron 48 | Openstack::NeutronClient.instance 49 | end 50 | 51 | def self.cinder 52 | Openstack::CinderClient.instance 53 | end 54 | 55 | def self.glance 56 | Openstack::GlanceClient.instance 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/client/request_logger.rb: -------------------------------------------------------------------------------- 1 | require 'log4r' 2 | require 'json' 3 | 4 | module VagrantPlugins 5 | module Openstack 6 | module HttpUtils 7 | module RequestLogger 8 | def log_request(method, url, body = nil, headers) 9 | @logger.debug "request => method : #{method}" 10 | @logger.debug "request => url : #{url}" 11 | @logger.debug "request => headers : #{headers}" 12 | @logger.debug "request => body : #{body}" unless body.nil? 13 | end 14 | 15 | def log_response(response) 16 | @logger.debug "response => code : #{response.code}" 17 | @logger.debug "response => headers : #{response.headers}" 18 | @logger.debug "response => body : #{response}" 19 | end 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/client/rest_utils.rb: -------------------------------------------------------------------------------- 1 | require 'restclient' 2 | 3 | module VagrantPlugins 4 | module Openstack 5 | module RestUtils 6 | def self._set_proxy(config) 7 | @logger = Log4r::Logger.new('vagrant_openstack::restutils') 8 | if config.http.proxy 9 | RestClient.proxy = config.http.proxy 10 | @logger.info "Setting up HTTP proxy to '#{config.http.proxy}'" 11 | end 12 | end 13 | 14 | def self.get(env, url, headers = {}, &block) 15 | config = env[:machine].provider_config 16 | _set_proxy(config) 17 | RestClient::Request.execute(method: :get, url: url, headers: headers, 18 | timeout: config.http.read_timeout, open_timeout: config.http.open_timeout, 19 | ssl_ca_file: config.ssl_ca_file, verify_ssl: config.ssl_verify_peer, &block) 20 | end 21 | 22 | def self.post(env, url, payload, headers = {}, &block) 23 | config = env[:machine].provider_config 24 | _set_proxy(config) 25 | RestClient::Request.execute(method: :post, url: url, payload: payload, headers: headers, 26 | timeout: config.http.read_timeout, open_timeout: config.http.open_timeout, 27 | ssl_ca_file: config.ssl_ca_file, verify_ssl: config.ssl_verify_peer, &block) 28 | end 29 | 30 | def self.delete(env, url, headers = {}, &block) 31 | config = env[:machine].provider_config 32 | _set_proxy(config) 33 | RestClient::Request.execute(method: :delete, url: url, headers: headers, 34 | timeout: config.http.read_timeout, open_timeout: config.http.open_timeout, 35 | ssl_ca_file: config.ssl_ca_file, verify_ssl: config.ssl_verify_peer, &block) 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/command/abstract_command.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/client/openstack' 2 | require 'colorize' 3 | 4 | module VagrantPlugins 5 | module Openstack 6 | module Command 7 | class AbstractCommand < Vagrant.plugin('2', :command) 8 | def initialize(argv, env) 9 | @env = env 10 | super(normalize_args(argv), env) 11 | end 12 | 13 | def execute(name) 14 | env = {} 15 | with_target_vms(nil, provider: :openstack) do |machine| 16 | env[:machine] = machine 17 | env[:ui] = @env.ui 18 | end 19 | 20 | before_cmd(name, @argv, env) 21 | 22 | cmd(name, @argv, env) 23 | @env.ui.info('') 24 | # rubocop:disable Lint/RescueException 25 | rescue Errors::VagrantOpenstackError, SystemExit, Interrupt => e 26 | raise e 27 | rescue Exception => e 28 | puts I18n.t('vagrant_openstack.global_error').red unless e.message && e.message.start_with?('Catched Error:') 29 | raise e 30 | end 31 | # rubocop:enable Lint/RescueException 32 | 33 | # 34 | # Before Vagrant 1.5, args list ends with an extra arg '--'. It removes it if present. 35 | # 36 | def normalize_args(args) 37 | return args if args.nil? 38 | args.pop if args.size > 0 && args.last == '--' 39 | args 40 | end 41 | 42 | def before_cmd(_name, _argv, _env) 43 | end 44 | 45 | def cmd(_name, _argv, _env) 46 | fail 'Command not implemented. \'cmd\' method must be overridden in all subclasses' 47 | end 48 | end 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/command/flavor_list.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/command/openstack_command' 2 | 3 | module VagrantPlugins 4 | module Openstack 5 | module Command 6 | class FlavorList < OpenstackCommand 7 | def self.synopsis 8 | I18n.t('vagrant_openstack.command.flavor_list_synopsis') 9 | end 10 | 11 | def cmd(name, argv, env) 12 | fail Errors::NoArgRequiredForCommand, cmd: name unless argv.size == 0 13 | flavors = env[:openstack_client].nova.get_all_flavors(env) 14 | 15 | rows = [] 16 | flavors.each do |f| 17 | rows << [f.id, f.name, f.vcpus, f.ram, f.disk] 18 | end 19 | display_table(env, ['ID', 'Name', 'vCPU', 'RAM (Mo)', 'Disk size (Go)'], rows) 20 | end 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/command/floatingip_list.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/command/openstack_command' 2 | 3 | module VagrantPlugins 4 | module Openstack 5 | module Command 6 | class FloatingIpList < OpenstackCommand 7 | def self.synopsis 8 | I18n.t('vagrant_openstack.command.flaotingip_list_synopsis') 9 | end 10 | 11 | def cmd(name, argv, env) 12 | fail Errors::NoArgRequiredForCommand, cmd: name unless argv.size == 0 13 | 14 | floating_ip_pools = env[:openstack_client].nova.get_floating_ip_pools(env) 15 | floating_ips = env[:openstack_client].nova.get_floating_ips(env) 16 | 17 | rows = [] 18 | floating_ip_pools.each do |floating_ip_pool| 19 | rows << [floating_ip_pool['name']] 20 | end 21 | display_table(env, ['Floating IP pools'], rows) 22 | 23 | rows = [] 24 | floating_ips.each do |floating_ip| 25 | rows << [floating_ip['id'], floating_ip['ip'], floating_ip['pool'], floating_ip['instance_id']] 26 | end 27 | display_table(env, ['ID', 'IP', 'Pool', 'Instance ID'], rows) 28 | end 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/command/image_list.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/command/openstack_command' 2 | 3 | module VagrantPlugins 4 | module Openstack 5 | module Command 6 | class ImageList < OpenstackCommand 7 | def self.synopsis 8 | I18n.t('vagrant_openstack.command.image_list_synopsis') 9 | end 10 | 11 | def cmd(name, argv, env) 12 | fail Errors::NoArgRequiredForCommand, cmd: name unless argv.size == 0 13 | rows = [] 14 | headers = %w(ID Name) 15 | if env[:openstack_client].session.endpoints.key? :image 16 | images = env[:openstack_client].glance.get_all_images(env) 17 | images.each { |i| rows << [i.id, i.name, i.visibility, i.size.to_i / 1024 / 1024, i.min_ram, i.min_disk] } 18 | headers << ['Visibility', 'Size (Mo)', 'Min RAM (Go)', 'Min Disk (Go)'] 19 | headers = headers.flatten 20 | else 21 | images = env[:openstack_client].nova.get_all_images(env) 22 | images.each { |image| rows << [image.id, image.name] } 23 | end 24 | display_table(env, headers, rows) 25 | end 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/command/main.rb: -------------------------------------------------------------------------------- 1 | module VagrantPlugins 2 | module Openstack 3 | module Command 4 | COMMANDS = [ 5 | { name: :'image-list', file: 'image_list', clazz: 'ImageList' }, 6 | { name: :'flavor-list', file: 'flavor_list', clazz: 'FlavorList' }, 7 | { name: :'network-list', file: 'network_list', clazz: 'NetworkList' }, 8 | { name: :'subnet-list', file: 'subnet_list', clazz: 'SubnetList' }, 9 | { name: :'floatingip-list', file: 'floatingip_list', clazz: 'FloatingIpList' }, 10 | { name: :'volume-list', file: 'volume_list', clazz: 'VolumeList' }, 11 | { name: :'reset', file: 'reset', clazz: 'Reset' } 12 | ] 13 | 14 | class Main < Vagrant.plugin('2', :command) 15 | def self.synopsis 16 | I18n.t('vagrant_openstack.command.main_synopsis') 17 | end 18 | 19 | def initialize(argv, env) 20 | @env = env 21 | @main_args, @sub_command, @sub_args = split_main_and_subcommand(argv) 22 | @commands = Vagrant::Registry.new 23 | 24 | COMMANDS.each do |cmd| 25 | @commands.register(cmd[:name]) do 26 | require_relative cmd[:file] 27 | Command.const_get(cmd[:clazz]) 28 | end 29 | end 30 | 31 | super(argv, env) 32 | end 33 | 34 | def execute 35 | command_class = @commands.get(@sub_command.to_sym) if @sub_command 36 | return usage unless command_class && @sub_command 37 | command_class.new(@sub_args, @env).execute(@sub_command) 38 | end 39 | 40 | def usage 41 | @env.ui.info I18n.t('vagrant_openstack.command.main_usage') 42 | @env.ui.info '' 43 | @env.ui.info I18n.t('vagrant_openstack.command.available_subcommands') 44 | @commands.each do |key, value| 45 | @env.ui.info " #{key.to_s.ljust(20)} #{value.synopsis}" 46 | end 47 | @env.ui.info '' 48 | end 49 | end 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/command/network_list.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/command/openstack_command' 2 | 3 | module VagrantPlugins 4 | module Openstack 5 | module Command 6 | class NetworkList < OpenstackCommand 7 | def self.synopsis 8 | I18n.t('vagrant_openstack.command.network_list_synopsis') 9 | end 10 | 11 | def cmd(name, argv, env) 12 | fail Errors::UnrecognizedArgForCommand, cmd: name, arg: argv[1] if argv.size > 1 13 | if argv.size == 0 14 | networks = env[:openstack_client].neutron.get_private_networks(env) 15 | elsif argv[0] == 'all' 16 | networks = env[:openstack_client].neutron.get_all_networks(env) 17 | else 18 | fail Errors::UnrecognizedArgForCommand, cmd: name, arg: argv[0] 19 | end 20 | display_item_list(env, networks) 21 | end 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/command/openstack_command.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/command/utils' 2 | require 'vagrant-openstack-provider/command/abstract_command' 3 | 4 | module VagrantPlugins 5 | module Openstack 6 | module Command 7 | class OpenstackCommand < AbstractCommand 8 | include VagrantPlugins::Openstack::Command::Utils 9 | 10 | def before_cmd(_name, _argv, env) 11 | VagrantPlugins::Openstack::Action::ConnectOpenstack.new(nil, env).call(env) 12 | end 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/command/reset.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/command/utils' 2 | require 'vagrant-openstack-provider/command/abstract_command' 3 | 4 | module VagrantPlugins 5 | module Openstack 6 | module Command 7 | class Reset < AbstractCommand 8 | def self.synopsis 9 | I18n.t('vagrant_openstack.command.reset') 10 | end 11 | 12 | def cmd(name, argv, env) 13 | fail Errors::NoArgRequiredForCommand, cmd: name unless argv.size == 0 14 | FileUtils.remove_dir("#{env[:machine].data_dir}") 15 | env[:ui].info 'Vagrant OpenStack Provider has been reset' 16 | end 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/command/subnet_list.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/command/openstack_command' 2 | 3 | module VagrantPlugins 4 | module Openstack 5 | module Command 6 | class SubnetList < OpenstackCommand 7 | def self.synopsis 8 | I18n.t('vagrant_openstack.command.subnet_list_synopsis') 9 | end 10 | 11 | def cmd(name, argv, env) 12 | fail Errors::NoArgRequiredForCommand, cmd: name unless argv.size == 0 13 | rows = [] 14 | env[:openstack_client].neutron.get_subnets(env).each do |subnet| 15 | rows << [subnet.id, subnet.name, subnet.cidr, subnet.enable_dhcp, subnet.network_id] 16 | end 17 | display_table(env, ['ID', 'Name', 'CIDR', 'DHCP', 'Network ID'], rows) 18 | end 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/command/utils.rb: -------------------------------------------------------------------------------- 1 | require 'terminal-table' 2 | 3 | module VagrantPlugins 4 | module Openstack 5 | module Command 6 | module Utils 7 | def display_item_list(env, items) 8 | rows = [] 9 | items.each do |item| 10 | rows << [item.id, item.name] 11 | end 12 | display_table(env, %w(ID Name), rows) 13 | end 14 | 15 | def display_table(env, headers, rows) 16 | table = Terminal::Table.new headings: headers, rows: rows 17 | env[:ui].info("\n#{table}") 18 | end 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/command/volume_list.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/command/openstack_command' 2 | 3 | module VagrantPlugins 4 | module Openstack 5 | module Command 6 | class VolumeList < OpenstackCommand 7 | def self.synopsis 8 | I18n.t('vagrant_openstack.command.volume_list_synopsis') 9 | end 10 | 11 | def cmd(name, argv, env) 12 | fail Errors::NoArgRequiredForCommand, cmd: name unless argv.size == 0 13 | volumes = env[:openstack_client].cinder.get_all_volumes(env) 14 | 15 | rows = [] 16 | volumes.each do |v| 17 | attachment = "#{v.instance_id} (#{v.device})" unless v.instance_id.nil? 18 | rows << [v.id, v.name, v.size, v.status, attachment] 19 | end 20 | display_table(env, ['ID', 'Name', 'Size (Go)', 'Status', 'Attachment (instance ID and device)'], rows) 21 | end 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/config/http.rb: -------------------------------------------------------------------------------- 1 | module VagrantPlugins 2 | module Openstack 3 | class HttpConfig 4 | UNSET_VALUE = Vagrant.plugin('2', :config).const_get(:UNSET_VALUE) 5 | 6 | # 7 | # @return [Integer] 8 | attr_accessor :open_timeout 9 | 10 | # 11 | # @return [Integer] 12 | attr_accessor :read_timeout 13 | 14 | # 15 | # @return [Integer] 16 | attr_accessor :proxy 17 | 18 | def initialize 19 | @open_timeout = UNSET_VALUE 20 | @read_timeout = UNSET_VALUE 21 | @proxy = UNSET_VALUE 22 | end 23 | 24 | def finalize! 25 | @open_timeout = 60 if @open_timeout == UNSET_VALUE 26 | @read_timeout = 30 if @read_timeout == UNSET_VALUE 27 | @proxy = nil if @proxy == UNSET_VALUE 28 | end 29 | 30 | def merge(other) 31 | result = self.class.new 32 | 33 | [self, other].each do |obj| 34 | obj.instance_variables.each do |key| 35 | next if key.to_s.start_with?('@__') 36 | 37 | value = obj.instance_variable_get(key) 38 | result.instance_variable_set(key, value) if value != UNSET_VALUE 39 | end 40 | end 41 | result 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/errors.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant' 2 | 3 | module VagrantPlugins 4 | module Openstack 5 | module Errors 6 | class VagrantOpenstackError < Vagrant::Errors::VagrantError 7 | # 8 | # Added for vagrant 1.4.x compatibility This attribute had been 9 | # added in Vagrant::Errors::VagrantError form the version 1.5.0 10 | # 11 | attr_accessor :extra_data 12 | 13 | def initialize(args = nil) 14 | @extra_data = args 15 | super(args) 16 | end 17 | 18 | error_namespace('vagrant_openstack.errors') 19 | error_key(:default) 20 | end 21 | 22 | class Timeout < VagrantOpenstackError 23 | error_key(:timeout) 24 | end 25 | 26 | class AuthenticationRequired < VagrantOpenstackError 27 | error_key(:authentication_required) 28 | end 29 | 30 | class AuthenticationFailed < VagrantOpenstackError 31 | error_key(:authentication_failed) 32 | end 33 | 34 | class BadAuthenticationEndpoint < VagrantOpenstackError 35 | error_key(:bad_authentication_endpoint) 36 | end 37 | 38 | class NoMatchingApiVersion < VagrantOpenstackError 39 | error_key(:no_matching_api_version) 40 | end 41 | 42 | class CreateBadState < VagrantOpenstackError 43 | error_key(:create_bad_state) 44 | end 45 | 46 | class NoMatchingFlavor < VagrantOpenstackError 47 | error_key(:no_matching_flavor) 48 | end 49 | 50 | class NoMatchingImage < VagrantOpenstackError 51 | error_key(:no_matching_image) 52 | end 53 | 54 | class ConflictBootVolume < VagrantOpenstackError 55 | error_key(:conflict_boot_volume) 56 | end 57 | 58 | class SyncMethodError < VagrantOpenstackError 59 | error_key(:sync_method_error) 60 | end 61 | 62 | class RsyncError < VagrantOpenstackError 63 | error_key(:rsync_error) 64 | end 65 | 66 | class SshUnavailable < VagrantOpenstackError 67 | error_key(:ssh_unavailble) 68 | end 69 | 70 | class NoArgRequiredForCommand < VagrantOpenstackError 71 | error_key(:no_arg_required_for_command) 72 | end 73 | 74 | class UnrecognizedArgForCommand < VagrantOpenstackError 75 | error_key(:unrecognized_arg_for_command) 76 | end 77 | 78 | class UnableToResolveFloatingIP < VagrantOpenstackError 79 | error_key(:unable_to_resolve_floating_ip) 80 | end 81 | 82 | class UnableToResolveIP < VagrantOpenstackError 83 | error_key(:unable_to_resolve_ip) 84 | end 85 | 86 | class UnableToResolveSSHKey < VagrantOpenstackError 87 | error_key(:unable_to_resolve_ssh_key) 88 | end 89 | 90 | class InvalidNetworkObject < VagrantOpenstackError 91 | error_key(:invalid_network_format) 92 | end 93 | 94 | class UnresolvedNetwork < VagrantOpenstackError 95 | error_key(:unresolved_network) 96 | end 97 | 98 | class UnresolvedNetworkId < VagrantOpenstackError 99 | error_key(:unresolved_network_id) 100 | end 101 | 102 | class UnresolvedNetworkName < VagrantOpenstackError 103 | error_key(:unresolved_network_name) 104 | end 105 | 106 | class ConflictNetworkNameId < VagrantOpenstackError 107 | error_key(:conflict_network_name_id) 108 | end 109 | 110 | class MultipleNetworkName < VagrantOpenstackError 111 | error_key(:multiple_network_name) 112 | end 113 | 114 | class InvalidVolumeObject < VagrantOpenstackError 115 | error_key(:invalid_volume_format) 116 | end 117 | 118 | class UnresolvedVolume < VagrantOpenstackError 119 | error_key(:unresolved_volume) 120 | end 121 | 122 | class UnresolvedVolumeId < VagrantOpenstackError 123 | error_key(:unresolved_volume_id) 124 | end 125 | 126 | class UnresolvedVolumeName < VagrantOpenstackError 127 | error_key(:unresolved_volume_name) 128 | end 129 | 130 | class ConflictVolumeNameId < VagrantOpenstackError 131 | error_key(:conflict_volume_name_id) 132 | end 133 | 134 | class MultipleVolumeName < VagrantOpenstackError 135 | error_key(:multiple_volume_name) 136 | end 137 | 138 | class MissingBootOption < VagrantOpenstackError 139 | error_key(:missing_boot_option) 140 | end 141 | 142 | class ConflictBootOption < VagrantOpenstackError 143 | error_key(:conflict_boot_option) 144 | end 145 | 146 | class NoMatchingSshUsername < VagrantOpenstackError 147 | error_key(:ssh_username_missing) 148 | end 149 | 150 | class InstanceNotFound < VagrantOpenstackError 151 | error_key(:instance_not_found) 152 | end 153 | 154 | class StackNotFound < VagrantOpenstackError 155 | error_key(:stack_not_found) 156 | end 157 | 158 | class NetworkServiceUnavailable < VagrantOpenstackError 159 | error_key(:nerwork_service_unavailable) 160 | end 161 | 162 | class VolumeServiceUnavailable < VagrantOpenstackError 163 | error_key(:volume_service_unavailable) 164 | end 165 | 166 | class FloatingIPAlreadyAssigned < VagrantOpenstackError 167 | error_key(:floating_ip_already_assigned) 168 | end 169 | 170 | class FloatingIPNotAvailable < VagrantOpenstackError 171 | error_key(:floating_ip_not_available) 172 | end 173 | 174 | class ServerStatusError < VagrantOpenstackError 175 | error_key(:server_status_error) 176 | end 177 | 178 | class StackStatusError < VagrantOpenstackError 179 | error_key(:stack_status_error) 180 | end 181 | 182 | class MissingNovaEndpoint < VagrantOpenstackError 183 | error_key(:missing_nova_endpoint) 184 | end 185 | end 186 | end 187 | end 188 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/logging.rb: -------------------------------------------------------------------------------- 1 | module VagrantPlugins 2 | module Openstack 3 | module Logging 4 | # This initializes the logging so that our logs are outputted at 5 | # the same level as Vagrant core logs. 6 | def self.init 7 | # Initialize logging 8 | level = nil 9 | begin 10 | level = Log4r.const_get(ENV['VAGRANT_LOG'].upcase) 11 | rescue NameError 12 | # This means that the logging constant wasn't found, 13 | # which is fine. We just keep `level` as `nil`. But 14 | # we tell the user. 15 | begin 16 | level = Log4r.const_get(ENV['VAGRANT_OPENSTACK_LOG'].upcase) 17 | rescue NameError 18 | level = nil 19 | end 20 | end 21 | 22 | # Some constants, such as "true" resolve to booleans, so the 23 | # above error checking doesn't catch it. This will check to make 24 | # sure that the log level is an integer, as Log4r requires. 25 | level = nil unless level.is_a?(Integer) 26 | 27 | # Set the logging level 28 | # logs as long as we have a valid level. 29 | if level 30 | logger = Log4r::Logger.new('vagrant_openstack') 31 | out = Log4r::Outputter.stdout 32 | out.formatter = Log4r::PatternFormatter.new(pattern: '%d | %5l | %m', date_pattern: '%Y-%m-%d %H:%M') 33 | logger.outputters = out 34 | logger.level = level 35 | end 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/plugin.rb: -------------------------------------------------------------------------------- 1 | begin 2 | require 'vagrant' 3 | rescue LoadError 4 | raise 'The Openstack Cloud provider must be run within Vagrant.' 5 | end 6 | 7 | require 'vagrant-openstack-provider/version_checker' 8 | 9 | # This is a sanity check to make sure no one is attempting to install 10 | # this into an early Vagrant version. 11 | if Vagrant::VERSION < '1.4.0' 12 | fail 'Openstack Cloud provider is only compatible with Vagrant 1.4+' 13 | end 14 | 15 | module VagrantPlugins 16 | module Openstack 17 | class Plugin < Vagrant.plugin('2') 18 | name 'Openstack Cloud' 19 | description <<-DESC 20 | This plugin enables Vagrant to manage machines in Openstack Cloud. 21 | DESC 22 | 23 | config(:openstack, :provider) do 24 | require_relative 'config' 25 | Config 26 | end 27 | 28 | provider(:openstack, box_optional: true, parallel: true) do 29 | Openstack.init_i18n 30 | Openstack.init_logging 31 | VagrantPlugins::Openstack.check_version 32 | 33 | # Load the actual provider 34 | require_relative 'provider' 35 | Provider 36 | end 37 | 38 | # TODO: Remove the if guard when Vagrant 1.8.0 is the minimum version. 39 | # rubocop:disable IndentationWidth 40 | if Gem::Version.new(Vagrant::VERSION) >= Gem::Version.new('1.8.0') 41 | provider_capability('openstack', 'snapshot_list') do 42 | require_relative 'cap/snapshot_list' 43 | Cap::SnapshotList 44 | end 45 | end 46 | # rubocop:enable IndentationWidth 47 | 48 | command('openstack') do 49 | Openstack.init_i18n 50 | Openstack.init_logging 51 | VagrantPlugins::Openstack.check_version 52 | 53 | require_relative 'command/main' 54 | Command::Main 55 | end 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/provider.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant' 2 | 3 | require 'vagrant-openstack-provider/action' 4 | 5 | module VagrantPlugins 6 | module Openstack 7 | class Provider < Vagrant.plugin('2', :provider) 8 | def initialize(machine) 9 | @machine = machine 10 | end 11 | 12 | def action(name) 13 | # Attempt to get the action method from the Action class if it 14 | # exists, otherwise return nil to show that we don't support the 15 | # given action. 16 | action_method = "action_#{name}" 17 | return Action.send(action_method) if Action.respond_to?(action_method) 18 | nil 19 | end 20 | 21 | def ssh_info 22 | # Run a custom action called "read_ssh_info" which does what it 23 | # says and puts the resulting SSH info into the `:machine_ssh_info` 24 | # key in the environment. 25 | env = @machine.action('read_ssh_info', lock: false) 26 | env[:machine_ssh_info] 27 | end 28 | 29 | def state 30 | # Run a custom action we define called "read_state" which does 31 | # what it says. It puts the state in the `:machine_state_id` 32 | # key in the environment. 33 | env = @machine.action('read_state', lock: false) 34 | 35 | state_id = env[:machine_state_id] 36 | 37 | # Get the short and long description 38 | short = I18n.t("vagrant_openstack.states.short_#{state_id}") 39 | long = I18n.t("vagrant_openstack.states.long_#{state_id}") 40 | 41 | # Return the MachineState object 42 | Vagrant::MachineState.new(state_id, short, long) 43 | end 44 | 45 | def to_s 46 | 'Openstack Cloud' 47 | end 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/utils.rb: -------------------------------------------------------------------------------- 1 | module VagrantPlugins 2 | module Openstack 3 | class Utils 4 | def initialize 5 | @logger = Log4r::Logger.new('vagrant_openstack::action::config_resolver') 6 | end 7 | 8 | def get_ip_address(env) 9 | addresses = env[:openstack_client].nova.get_server_details(env, env[:machine].id)['addresses'] 10 | addresses.each do |_, network| 11 | network.each do |network_detail| 12 | return network_detail['addr'] if network_detail['OS-EXT-IPS:type'] == 'floating' 13 | end 14 | end 15 | fail Errors::UnableToResolveIP if addresses.size == 0 16 | if addresses.size == 1 || !env[:machine].provider_config.networks 17 | net_addresses = addresses.first[1] 18 | else 19 | first_network = env[:machine].provider_config.networks[0] 20 | if first_network.is_a? String 21 | net_addresses = addresses[first_network] 22 | else 23 | net_addresses = addresses[first_network[:name]] 24 | end 25 | end 26 | fail Errors::UnableToResolveIP if net_addresses.size == 0 27 | net_addresses[0]['addr'] 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/version.rb: -------------------------------------------------------------------------------- 1 | module VagrantPlugins 2 | module Openstack 3 | # 4 | # Stable versions must respect the pattern given 5 | # by VagrantPlugins::Openstack::VERSION_PATTERN 6 | # 7 | VERSION = '0.13.0' 8 | 9 | # 10 | # Stable version must respect the naming convention 'x.y.z' 11 | # where x, y and z are integers inside the range [0, 999] 12 | # 13 | VERSION_PATTERN = /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/ 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /source/lib/vagrant-openstack-provider/version_checker.rb: -------------------------------------------------------------------------------- 1 | require 'colorize' 2 | require 'singleton' 3 | require 'vagrant-openstack-provider/version' 4 | 5 | module VagrantPlugins 6 | module Openstack 7 | class VersionChecker 8 | include Singleton 9 | 10 | # 11 | # :latest, :outdated or :unstable 12 | # 13 | # A version is considered unstable if it does not 14 | # respect the pattern or if it is greater than the 15 | # latest from rubygem 16 | # 17 | attr_accessor :status 18 | 19 | # 20 | # boolean attribute to disbale version checker 21 | # 22 | attr_accessor :check_enabled 23 | 24 | def initialize 25 | @status = nil 26 | @check_enabled = true 27 | 28 | check = ENV['VAGRANT_OPENSTACK_VERSION_CKECK'] 29 | @check_enabled = false if check && check.upcase == 'DISABLED' 30 | end 31 | 32 | # 33 | # Check the latest version from rubygem and set the status 34 | # 35 | def check 36 | return :latest unless @check_enabled 37 | return @status unless @status.nil? 38 | 39 | begin 40 | latest = Gem.latest_spec_for('vagrant-openstack-provider').version.version 41 | rescue 42 | # If for any reason the version of the latest pulished 43 | # version can't be found we don't fail in any way 44 | return :latest 45 | end 46 | 47 | current = VagrantPlugins::Openstack::VERSION 48 | 49 | unless current =~ VERSION_PATTERN 50 | @status = :unstable 51 | print_message I18n.t('vagrant_openstack.version_unstable') 52 | return 53 | end 54 | 55 | if latest.eql? current 56 | @status = :latest 57 | return 58 | end 59 | 60 | v_latest = latest.split('.').map(&:to_i) 61 | v_current = current.split('.').map(&:to_i) 62 | 63 | i_latest = v_latest[2] + v_latest[1] * 1000 + v_latest[0] * 1_000_000 64 | i_current = v_current[2] + v_current[1] * 1000 + v_current[0] * 1_000_000 65 | 66 | if i_current > i_latest 67 | @status = :unstable 68 | print_message I18n.t('vagrant_openstack.version_unstable') 69 | return 70 | end 71 | 72 | @status = :outdated 73 | print_message I18n.t('vagrant_openstack.version_outdated', latest: latest, current: current) 74 | end 75 | 76 | private 77 | 78 | def print_message(message) 79 | $stderr.puts message.yellow 80 | $stderr.puts '' 81 | end 82 | end 83 | 84 | # rubocop:disable Lint/HandleExceptions 85 | def self.check_version 86 | Timeout.timeout(3, Errors::Timeout) do 87 | VersionChecker.instance.check 88 | end 89 | rescue 90 | # Do nothing whatever the failure cause 91 | end 92 | # rubocop:enable Lint/HandleExceptions 93 | end 94 | end 95 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/action/create_stack_spec.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/spec_helper' 2 | require 'ostruct' 3 | require 'sshkey' 4 | 5 | include VagrantPlugins::Openstack::Action 6 | include VagrantPlugins::Openstack::HttpUtils 7 | include VagrantPlugins::Openstack::Domain 8 | 9 | describe VagrantPlugins::Openstack::Action::CreateStack do 10 | let(:config) do 11 | double('config').tap do |config| 12 | config.stub(:stacks) do 13 | [ 14 | { 15 | name: 'stack1', 16 | template: 'template.yml' 17 | }, 18 | { 19 | name: 'stack2', 20 | template: 'template.yml' 21 | } 22 | ] 23 | end 24 | config.stub(:stack_create_timeout) { 200 } 25 | end 26 | end 27 | 28 | let(:heat) do 29 | double('heat') 30 | end 31 | 32 | let(:env) do 33 | {}.tap do |env| 34 | env[:ui] = double('ui') 35 | env[:ui].stub(:info).with(anything) 36 | env[:machine] = double('machine') 37 | env[:machine] = OpenStruct.new.tap do |m| 38 | m.provider_config = config 39 | m.id = nil 40 | end 41 | env[:openstack_client] = double('openstack_client') 42 | env[:openstack_client].stub(:heat) { heat } 43 | end 44 | end 45 | before :each do 46 | CreateStack.send(:public, *CreateStack.private_instance_methods) 47 | app = double('app') 48 | app.stub(:call).with(anything) 49 | @action = CreateStack.new(app, nil) 50 | YAML.stub(:load_file).with('template.yml').and_return(YAML.load(' 51 | heat_template_version: 2013-05-23 52 | 53 | description: Simple template to deploy a single compute instance 54 | 55 | resources: 56 | my_instance: 57 | type: OS::Nova::Server 58 | properties: 59 | key_name: julien-mac 60 | image: CoreOS 61 | flavor: 1_vCPU_RAM_512M_HD_10G 62 | ')) 63 | end 64 | 65 | describe 'call' do 66 | it 'should create stacks on heat twice' do 67 | heat.stub(:create_stack).and_return('idstack') 68 | File.should_receive(:write).with('/stack_stack1_id', 'idstack') 69 | File.should_receive(:write).with('/stack_stack2_id', 'idstack') 70 | # TODO(julienvey) assert content of create call is correct 71 | heat.should_receive(:create_stack).exactly(2).times 72 | heat.stub(:get_stack_details).and_return('stack_status' => 'CREATE_COMPLETE') 73 | @action.call(env) 74 | end 75 | end 76 | 77 | describe 'waiting_for_server_to_be_built' do 78 | context 'when server is not yet active' do 79 | it 'become active after one retry' do 80 | heat.stub(:get_stack_details).and_return({ 'stack_status' => 'CREATE_IN_PROGRESS' }, 'stack_status' => 'CREATE_COMPLETE') 81 | heat.should_receive(:get_stack_details).with(env, 'stack1', 'id-01').exactly(2).times 82 | config.stub(:stack_create_timeout) { 5 } 83 | @action.waiting_for_stack_to_be_created(env, 'stack1', 'id-01', 1) 84 | end 85 | it 'timeout before the server become active' do 86 | heat.stub(:get_stack_details).and_return({ 'stack_status' => 'CREATE_IN_PROGRESS' }, 'stack_status' => 'CREATE_IN_PROGRESS') 87 | heat.should_receive(:get_stack_details).with(env, 'stack1', 'id-01').at_least(2).times 88 | config.stub(:stack_create_timeout) { 3 } 89 | expect { @action.waiting_for_stack_to_be_created(env, 'stack1', 'id-01', 1) }.to raise_error Errors::Timeout 90 | end 91 | it 'raise an error after one retry' do 92 | heat.stub(:get_stack_details).and_return({ 'stack_status' => 'CREATE_IN_PROGRESS' }, 'stack_status' => 'CREATE_FAILED') 93 | heat.should_receive(:get_stack_details).with(env, 'stack1', 'id-01').exactly(2).times 94 | config.stub(:stack_create_timeout) { 3 } 95 | expect { @action.waiting_for_stack_to_be_created(env, 'stack1', 'id-01', 1) }.to raise_error Errors::StackStatusError 96 | end 97 | end 98 | end 99 | end 100 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/action/delete_server_spec.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/spec_helper' 2 | 3 | describe VagrantPlugins::Openstack::Action::DeleteServer do 4 | let(:nova) do 5 | double('nova').tap do |app| 6 | app.stub(:delete_server) 7 | app.stub(:delete_keypair_if_vagrant) 8 | end 9 | end 10 | 11 | let(:openstack_client) do 12 | double('openstack_client').tap do |os| 13 | os.stub(:nova) { nova } 14 | end 15 | end 16 | 17 | let(:config) do 18 | double('config') 19 | end 20 | 21 | let(:env) do 22 | {}.tap do |env| 23 | env[:ui] = double('ui') 24 | env[:ui].stub(:info).with(anything) 25 | env[:ui].stub(:error).with(anything) 26 | env[:openstack_client] = openstack_client 27 | env[:machine] = OpenStruct.new.tap do |m| 28 | m.provider_config = config 29 | m.id = 'server_id' 30 | end 31 | end 32 | end 33 | 34 | before :each do 35 | DeleteServer.send(:public, *DeleteServer.private_instance_methods) 36 | app = double('app') 37 | app.stub(:call).with(anything) 38 | @action = DeleteServer.new(app, nil) 39 | end 40 | 41 | describe 'call' do 42 | context 'when id is present' do 43 | it 'delete server' do 44 | expect(nova).to receive(:delete_server).with(env, 'server_id') 45 | expect(nova).to receive(:delete_keypair_if_vagrant).with(env, 'server_id') 46 | expect(@action).to receive(:waiting_for_instance_to_be_deleted).with(env, 'server_id') 47 | @action.call(env) 48 | end 49 | end 50 | context 'when id is not present' do 51 | it 'delete server' do 52 | env[:machine].id = nil 53 | expect(nova).should_not_receive(:delete_server) 54 | expect(nova).should_not_receive(:delete_keypair_if_vagrant) 55 | @action.call(env) 56 | end 57 | end 58 | end 59 | 60 | describe 'waiting_for_instance_to_be_deleted' do 61 | context 'when server is not yet active' do 62 | it 'become deleted after one retry' do 63 | nova.stub(:get_server_details).once.and_return('status' => 'ACTIVE') 64 | nova.stub(:get_server_details).once.and_raise(Errors::InstanceNotFound) 65 | nova.should_receive(:get_server_details).with(env, 'server-01').exactly(1).times 66 | config.stub(:server_delete_timeout) { 5 } 67 | @action.waiting_for_instance_to_be_deleted(env, 'server-01', 1) 68 | end 69 | it 'become deleted after one retry' do 70 | nova.stub(:get_server_details).and_return({ 'status' => 'ACTIVE' }, 'status' => 'DELETED') 71 | nova.should_receive(:get_server_details).with(env, 'server-01').exactly(2).times 72 | config.stub(:server_delete_timeout) { 5 } 73 | @action.waiting_for_instance_to_be_deleted(env, 'server-01', 1) 74 | end 75 | it 'timeout before the server become active' do 76 | nova.stub(:get_server_details).and_return({ 'status' => 'ACTIVE' }, 'status' => 'ACTIVE') 77 | nova.should_receive(:get_server_details).with(env, 'server-01').at_least(2).times 78 | config.stub(:server_delete_timeout) { 3 } 79 | expect { @action.waiting_for_instance_to_be_deleted(env, 'server-01', 1) }.to raise_error Errors::Timeout 80 | end 81 | it 'raise an error after one retry' do 82 | nova.stub(:get_server_details).and_return({ 'status' => 'ACTIVE' }, 'status' => 'ERROR') 83 | nova.should_receive(:get_server_details).with(env, 'server-01').exactly(2).times 84 | config.stub(:server_delete_timeout) { 3 } 85 | expect { @action.waiting_for_instance_to_be_deleted(env, 'server-01', 1) }.to raise_error Errors::ServerStatusError 86 | end 87 | end 88 | end 89 | end 90 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/action/delete_stack_spec.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/spec_helper' 2 | 3 | describe VagrantPlugins::Openstack::Action::DeleteStack do 4 | let(:heat) do 5 | double('heat').tap do |app| 6 | app.stub(:delete_stack) 7 | end 8 | end 9 | 10 | let(:openstack_client) do 11 | double('openstack_client').tap do |os| 12 | os.stub(:heat) { heat } 13 | end 14 | end 15 | 16 | let(:env) do 17 | {}.tap do |env| 18 | env[:ui] = double('ui') 19 | env[:ui].stub(:info).with(anything) 20 | env[:ui].stub(:error).with(anything) 21 | env[:openstack_client] = openstack_client 22 | env[:machine] = OpenStruct.new.tap do |m| 23 | m.id = 'server_id' 24 | m.data_dir = '/test' 25 | end 26 | end 27 | end 28 | 29 | before :each do 30 | DeleteStack.send(:public, *DeleteStack.private_instance_methods) 31 | app = double('app') 32 | app.stub(:call).with(anything) 33 | @action = DeleteStack.new(app, nil) 34 | end 35 | 36 | describe 'call' do 37 | context 'when id is present' do 38 | it 'delete stack' do 39 | expect(heat).to receive(:delete_stack).with(env, 'test1', '1234') 40 | expect(heat).to receive(:delete_stack).with(env, 'test2', '2345') 41 | @action.stub(:list_stack_files).with(env).and_return([ 42 | { 43 | name: 'test1', 44 | id: '1234' 45 | }, { 46 | name: 'test2', 47 | id: '2345' 48 | }]) 49 | expect(@action).to receive(:waiting_for_stack_to_be_deleted).with(env, 'test1', '1234') 50 | expect(@action).to receive(:waiting_for_stack_to_be_deleted).with(env, 'test2', '2345') 51 | @action.call(env) 52 | end 53 | end 54 | context 'when id is not present' do 55 | it 'delete stack' do 56 | @action.stub(:list_stack_files).with(env).and_return([]) 57 | expect(heat).should_not_receive(:delete_stack) 58 | expect(heat).should_not_receive(:waiting_for_stack_to_be_deleted) 59 | @action.call(env) 60 | end 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/action/message_spec.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/spec_helper' 2 | 3 | describe VagrantPlugins::Openstack::Action::Message do 4 | let(:ui) do 5 | double('ui').tap do |ui| 6 | ui.stub(:info).with(anything) 7 | ui.stub(:error).with(anything) 8 | end 9 | end 10 | 11 | let(:env) do 12 | {}.tap do |env| 13 | env[:ui] = ui 14 | end 15 | end 16 | 17 | let(:app) do 18 | double('app').tap do |app| 19 | app.stub(:call).with(anything) 20 | end 21 | end 22 | 23 | describe 'call' do 24 | context 'when message is given' do 25 | it 'print out the message' do 26 | expect(ui).to receive(:info).with('Message to show') 27 | expect(app).to receive(:call) 28 | @action = Message.new(app, nil, 'Message to show') 29 | @action.call(env) 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/action/provision_spec.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/spec_helper' 2 | 3 | # 4 | # Stubing all the interactions using the real 5 | # provisioner classes is somehow complicated... 6 | # 7 | class FakeProvisioner 8 | def provision 9 | end 10 | end 11 | 12 | class FakeShellProvisioner < FakeProvisioner 13 | attr_accessor :config 14 | 15 | def initialize(config) 16 | @config = config 17 | end 18 | end 19 | 20 | describe VagrantPlugins::Openstack::Action::ProvisionWrapper do 21 | let(:app) do 22 | double 23 | end 24 | 25 | let(:internal_provision_wrapper) do 26 | double 27 | end 28 | 29 | before :each do 30 | # Stub lookup for provisioners and return a hash containing the test mock. 31 | allow(Vagrant.plugin('2').manager).to receive(:provisioners).and_return(shell: FakeShellProvisioner) 32 | @action = ProvisionWrapper.new(app, nil) 33 | end 34 | 35 | describe 'execute' do 36 | it 'call InternalProvisionWrapper and conitnue the middleware chain' do 37 | expect(internal_provision_wrapper).to receive(:call) 38 | InternalProvisionWrapper.stub(:new) { internal_provision_wrapper } 39 | app.stub(:call) {} 40 | @action.execute nil 41 | end 42 | end 43 | end 44 | 45 | describe VagrantPlugins::Openstack::Action::InternalProvisionWrapper do 46 | let(:env) do 47 | {} 48 | end 49 | 50 | before :each do 51 | # Stub lookup for provisioners and return a hash containing the test mock. 52 | allow(Vagrant.plugin('2').manager).to receive(:provisioners).and_return(shell: FakeShellProvisioner) 53 | @action = InternalProvisionWrapper.new(nil, env) 54 | end 55 | 56 | describe 'run_provisioner' do 57 | context 'when running a shell provisioner' do 58 | context 'without meta-arg' do 59 | it 'does not change the provisioner config' do 60 | env[:provisioner] = FakeShellProvisioner.new(OpenStruct.new.tap do |c| 61 | c.args = %w(arg1 arg2) 62 | end) 63 | 64 | expect(env[:provisioner]).to receive(:provision) 65 | expect(@action).to receive(:handle_shell_meta_args) 66 | 67 | @action.run_provisioner(env) 68 | expect(env[:provisioner].config.args).to eq(%w(arg1 arg2)) 69 | end 70 | end 71 | 72 | context 'with @@ssh_ip@@ meta-arg' do 73 | it 'replace the meta-args in the provisioner config' do 74 | env[:provisioner] = FakeShellProvisioner.new(OpenStruct.new.tap do |c| 75 | c.args = ['arg1', '@@ssh_ip@@', 'arg3'] 76 | end) 77 | 78 | VagrantPlugins::Openstack::Action.stub(:get_ssh_info).and_return host: '192.168.0.1' 79 | expect(env[:provisioner]).to receive(:provision) 80 | 81 | @action.run_provisioner(env) 82 | expect(env[:provisioner].config.args).to eq(%w(arg1 192.168.0.1 arg3)) 83 | end 84 | end 85 | end 86 | 87 | context 'when running a provisioner other that the shell provisioner' do 88 | it 'does not call handle_shell_meta_args' do 89 | env[:provisioner] = FakeProvisioner.new 90 | expect(@action).should_not_receive(:handle_shell_meta_args) 91 | expect(env[:provisioner]).to receive(:provision) 92 | 93 | @action.run_provisioner(env) 94 | end 95 | end 96 | end 97 | end 98 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/action/read_state_spec.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/spec_helper' 2 | 3 | describe VagrantPlugins::Openstack::Action::ReadState do 4 | let(:nova) do 5 | double('nova') 6 | end 7 | 8 | let(:env) do 9 | {}.tap do |env| 10 | env[:ui] = double('ui').tap do |ui| 11 | ui.stub(:info).with(anything) 12 | ui.stub(:error).with(anything) 13 | end 14 | env[:openstack_client] = double('openstack_client').tap do |os| 15 | os.stub(:nova) { nova } 16 | end 17 | env[:machine] = OpenStruct.new 18 | end 19 | end 20 | 21 | let(:app) do 22 | double('app').tap do |app| 23 | app.stub(:call).with(anything) 24 | end 25 | end 26 | 27 | describe 'call' do 28 | context 'when server id is present' do 29 | it 'set the state to the vm_state returned by nova' do 30 | env[:machine].id = 'server_id' 31 | nova.stub(:get_server_details).and_return('status' => 'ACTIVE') 32 | 33 | expect(nova).to receive(:get_server_details).with(env, 'server_id') 34 | expect(app).to receive(:call) 35 | 36 | @action = ReadState.new(app, nil) 37 | @action.call(env) 38 | 39 | expect(env[:machine_state_id]).to eq(:active) 40 | end 41 | it 'set the state to the task_state returned by nova extension' do 42 | env[:machine].id = 'server_id' 43 | nova.stub(:get_server_details).and_return('OS-EXT-STS:task_state' => 'SUSPENDING') 44 | 45 | expect(nova).to receive(:get_server_details).with(env, 'server_id') 46 | expect(app).to receive(:call) 47 | 48 | @action = ReadState.new(app, nil) 49 | @action.call(env) 50 | 51 | expect(env[:machine_state_id]).to eq(:suspending) 52 | end 53 | end 54 | context 'when server id is not present' do 55 | it 'set the state to :not_created' do 56 | env[:machine].id = nil 57 | expect(nova).to_not receive(:get_server_details) 58 | expect(app).to receive(:call) 59 | 60 | @action = ReadState.new(app, nil) 61 | @action.call(env) 62 | 63 | expect(env[:machine_state_id]).to eq(:not_created) 64 | end 65 | end 66 | context 'when server cannot be found' do 67 | it 'set the state to :not_created' do 68 | env[:machine].id = 'server_id' 69 | nova.stub(:get_server_details).and_return(nil) 70 | 71 | expect(nova).to receive(:get_server_details).with(env, 'server_id') 72 | expect(app).to receive(:call) 73 | 74 | @action = ReadState.new(app, nil) 75 | @action.call(env) 76 | 77 | expect(env[:machine_state_id]).to eq(:not_created) 78 | end 79 | end 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/action/resume_server_spec.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/spec_helper' 2 | 3 | describe VagrantPlugins::Openstack::Action::Resume do 4 | let(:nova) do 5 | double('nova').tap do |nova| 6 | nova.stub(:resume_server) 7 | end 8 | end 9 | 10 | let(:env) do 11 | {}.tap do |env| 12 | env[:ui] = double('ui').tap do |ui| 13 | ui.stub(:info).with(anything) 14 | ui.stub(:error).with(anything) 15 | end 16 | env[:openstack_client] = double('openstack_client').tap do |os| 17 | os.stub(:nova) { nova } 18 | end 19 | env[:machine] = OpenStruct.new 20 | end 21 | end 22 | 23 | let(:app) do 24 | double('app').tap do |app| 25 | app.stub(:call).with(anything) 26 | end 27 | end 28 | 29 | describe 'call' do 30 | context 'when server id is present' do 31 | it 'starts the server' do 32 | env[:machine].id = 'server_id' 33 | expect(nova).to receive(:resume_server).with(env, 'server_id') 34 | expect(app).to receive(:call) 35 | @action = Resume.new(app, nil) 36 | @action.call(env) 37 | end 38 | end 39 | context 'when server id is not present' do 40 | it 'does nothing' do 41 | env[:machine].id = nil 42 | expect(nova).to_not receive(:resume_server) 43 | expect(app).to receive(:call) 44 | @action = Resume.new(app, nil) 45 | @action.call(env) 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/action/start_server_spec.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/spec_helper' 2 | 3 | describe VagrantPlugins::Openstack::Action::StartServer do 4 | let(:nova) do 5 | double('nova').tap do |nova| 6 | nova.stub(:start_server) 7 | end 8 | end 9 | 10 | let(:env) do 11 | {}.tap do |env| 12 | env[:ui] = double('ui').tap do |ui| 13 | ui.stub(:info).with(anything) 14 | ui.stub(:error).with(anything) 15 | end 16 | env[:openstack_client] = double('openstack_client').tap do |os| 17 | os.stub(:nova) { nova } 18 | end 19 | env[:machine] = OpenStruct.new 20 | end 21 | end 22 | 23 | let(:app) do 24 | double('app').tap do |app| 25 | app.stub(:call).with(anything) 26 | end 27 | end 28 | 29 | describe 'call' do 30 | context 'when server id is present' do 31 | it 'starts the server' do 32 | env[:machine].id = 'server_id' 33 | expect(nova).to receive(:start_server).with(env, 'server_id') 34 | expect(app).to receive(:call) 35 | @action = StartServer.new(app, nil) 36 | @action.call(env) 37 | end 38 | end 39 | context 'when server id is not present' do 40 | it 'does nothing' do 41 | env[:machine].id = nil 42 | expect(nova).to_not receive(:start_server) 43 | expect(app).to receive(:call) 44 | @action = StartServer.new(app, nil) 45 | @action.call(env) 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/action/stop_server_spec.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/spec_helper' 2 | 3 | describe VagrantPlugins::Openstack::Action::StopServer do 4 | let(:nova) do 5 | double('nova').tap do |nova| 6 | nova.stub(:stop_server) 7 | end 8 | end 9 | 10 | let(:env) do 11 | {}.tap do |env| 12 | env[:ui] = double('ui').tap do |ui| 13 | ui.stub(:info).with(anything) 14 | ui.stub(:error).with(anything) 15 | end 16 | env[:openstack_client] = double('openstack_client').tap do |os| 17 | os.stub(:nova) { nova } 18 | end 19 | env[:machine] = OpenStruct.new 20 | end 21 | end 22 | 23 | let(:app) do 24 | double('app').tap do |app| 25 | app.stub(:call).with(anything) 26 | end 27 | end 28 | 29 | describe 'call' do 30 | context 'when server id is present' do 31 | it 'stops the server' do 32 | env[:machine].id = 'server_id' 33 | expect(nova).to receive(:stop_server).with(env, 'server_id') 34 | expect(app).to receive(:call) 35 | @action = StopServer.new(app, nil) 36 | @action.call(env) 37 | end 38 | end 39 | context 'when server id is not present' do 40 | it 'does nothing' do 41 | env[:machine].id = nil 42 | expect(nova).to_not receive(:stop_server) 43 | expect(app).to receive(:call) 44 | @action = StopServer.new(app, nil) 45 | @action.call(env) 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/action/suspend_server_spec.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/spec_helper' 2 | 3 | describe VagrantPlugins::Openstack::Action::Suspend do 4 | let(:nova) do 5 | double('nova').tap do |nova| 6 | nova.stub(:suspend_server) 7 | end 8 | end 9 | 10 | let(:env) do 11 | {}.tap do |env| 12 | env[:ui] = double('ui').tap do |ui| 13 | ui.stub(:info).with(anything) 14 | ui.stub(:error).with(anything) 15 | end 16 | env[:openstack_client] = double('openstack_client').tap do |os| 17 | os.stub(:nova) { nova } 18 | end 19 | env[:machine] = OpenStruct.new 20 | end 21 | end 22 | 23 | let(:app) do 24 | double('app').tap do |app| 25 | app.stub(:call).with(anything) 26 | end 27 | end 28 | 29 | describe 'call' do 30 | context 'when server id is present' do 31 | it 'starts the server' do 32 | env[:machine].id = 'server_id' 33 | expect(nova).to receive(:suspend_server).with(env, 'server_id') 34 | expect(app).to receive(:call) 35 | @action = Suspend.new(app, nil) 36 | @action.call(env) 37 | end 38 | end 39 | context 'when server id is not present' do 40 | it 'does nothing' do 41 | env[:machine].id = nil 42 | expect(nova).to_not receive(:suspend_server) 43 | expect(app).to receive(:call) 44 | @action = Suspend.new(app, nil) 45 | @action.call(env) 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/action/wait_active_spec.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/spec_helper' 2 | 3 | describe VagrantPlugins::Openstack::Action::WaitForServerToBeActive do 4 | let(:nova) do 5 | double('nova') 6 | end 7 | 8 | let(:config) do 9 | double('config') 10 | end 11 | 12 | let(:env) do 13 | {}.tap do |env| 14 | env[:ui] = double('ui').tap do |ui| 15 | ui.stub(:info).with(anything) 16 | ui.stub(:error).with(anything) 17 | end 18 | env[:openstack_client] = double('openstack_client').tap do |os| 19 | os.stub(:nova) { nova } 20 | end 21 | env[:machine] = OpenStruct.new.tap do |m| 22 | m.provider_config = config 23 | m.id = 'server_id' 24 | end 25 | end 26 | end 27 | 28 | let(:app) do 29 | double('app').tap do |app| 30 | app.stub(:call).with(anything) 31 | end 32 | end 33 | 34 | describe 'call' do 35 | context 'when server is not yet active' do 36 | it 'become active after one retry' do 37 | nova.stub(:get_server_details).and_return({ 'status' => 'BUILD' }, 'status' => 'ACTIVE') 38 | expect(nova).to receive(:get_server_details).with(env, 'server_id').exactly(2).times 39 | expect(app).to receive(:call) 40 | config.stub(:server_active_timeout) { 5 } 41 | @action = WaitForServerToBeActive.new(app, nil, 1) 42 | @action.call(env) 43 | end 44 | it 'timeout after one retry' do 45 | nova.stub(:get_server_details).and_return({ 'status' => 'BUILD' }, 'status' => 'BUILD') 46 | expect(nova).to receive(:get_server_details).with(env, 'server_id').at_least(2).times 47 | config.stub(:server_active_timeout) { 2 } 48 | @action = WaitForServerToBeActive.new(app, nil, 1) 49 | expect { @action.call(env) }.to raise_error Errors::Timeout 50 | end 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/action/wait_stop_spec.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/spec_helper' 2 | 3 | describe VagrantPlugins::Openstack::Action::WaitForServerToStop do 4 | let(:nova) do 5 | double('nova') 6 | end 7 | 8 | let(:config) do 9 | double('config') 10 | end 11 | 12 | let(:env) do 13 | {}.tap do |env| 14 | env[:ui] = double('ui').tap do |ui| 15 | ui.stub(:info).with(anything) 16 | ui.stub(:error).with(anything) 17 | end 18 | env[:openstack_client] = double('openstack_client').tap do |os| 19 | os.stub(:nova) { nova } 20 | end 21 | env[:machine] = OpenStruct.new.tap do |m| 22 | m.provider_config = config 23 | m.id = 'server_id' 24 | end 25 | end 26 | end 27 | 28 | let(:app) do 29 | double('app').tap do |app| 30 | app.stub(:call).with(anything) 31 | end 32 | end 33 | 34 | describe 'call' do 35 | context 'when server is active' do 36 | it 'become shutoff after one retry' do 37 | nova.stub(:get_server_details).and_return({ 'status' => 'ACTIVE' }, 'status' => 'SHUTOFF') 38 | expect(nova).to receive(:get_server_details).with(env, 'server_id').exactly(2).times 39 | expect(app).to receive(:call) 40 | config.stub(:server_stop_timeout) { 5 } 41 | @action = WaitForServerToStop.new(app, nil, 1) 42 | @action.call(env) 43 | end 44 | it 'timeout after one retry' do 45 | nova.stub(:get_server_details).and_return({ 'status' => 'ACTIVE' }, 'status' => 'ACTIVE') 46 | expect(nova).to receive(:get_server_details).with(env, 'server_id').at_least(2).times 47 | config.stub(:server_stop_timeout) { 2 } 48 | @action = WaitForServerToStop.new(app, nil, 1) 49 | expect { @action.call(env) }.to raise_error Errors::Timeout 50 | end 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/action_spec.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/spec_helper' 2 | 3 | describe VagrantPlugins::Openstack::Action do 4 | let(:builder) do 5 | double('builder').tap do |builder| 6 | builder.stub(:use) 7 | end 8 | end 9 | 10 | before :each do 11 | Action.stub(:new_builder) { builder } 12 | end 13 | 14 | describe 'action_destroy' do 15 | it 'add others middleware to builder' do 16 | expect(builder).to receive(:use).with(ConfigValidate) 17 | expect(builder).to receive(:use).with(ConnectOpenstack) 18 | expect(builder).to receive(:use).with(Call, ReadState) 19 | # TODO, Impove this test to check what's happen after ReadState 20 | Action.action_destroy 21 | end 22 | end 23 | 24 | describe 'action_provision' do 25 | it 'add others middleware to builder' do 26 | expect(builder).to receive(:use).with(ConfigValidate) 27 | expect(builder).to receive(:use).with(ConnectOpenstack) 28 | expect(builder).to receive(:use).with(Call, ReadState) 29 | # TODO, Impove this test to check what's happen after ReadState 30 | Action.action_provision 31 | end 32 | end 33 | 34 | describe 'action_read_ssh_info' do 35 | it 'add others middleware to builder' do 36 | expect(builder).to receive(:use).with(ConfigValidate) 37 | expect(builder).to receive(:use).with(ConnectOpenstack) 38 | expect(builder).to receive(:use).with(ReadSSHInfo) 39 | Action.action_read_ssh_info 40 | end 41 | end 42 | 43 | describe 'action_read_state' do 44 | it 'add others middleware to builder' do 45 | expect(builder).to receive(:use).with(ConfigValidate) 46 | expect(builder).to receive(:use).with(ConnectOpenstack) 47 | expect(builder).to receive(:use).with(ReadState) 48 | Action.action_read_state 49 | end 50 | end 51 | 52 | describe 'action_ssh' do 53 | it 'add others middleware to builder' do 54 | expect(builder).to receive(:use).with(ConfigValidate) 55 | expect(builder).to receive(:use).with(ConnectOpenstack) 56 | expect(builder).to receive(:use).with(Call, ReadState) 57 | Action.action_ssh 58 | end 59 | end 60 | 61 | describe 'action_ssh_run' do 62 | it 'add others middleware to builder' do 63 | expect(builder).to receive(:use).with(ConfigValidate) 64 | expect(builder).to receive(:use).with(ConnectOpenstack) 65 | expect(builder).to receive(:use).with(Call, ReadState) 66 | # TODO, Impove this test to check what's happen after ReadState 67 | Action.action_ssh_run 68 | end 69 | end 70 | 71 | describe 'action_up' do 72 | it 'add others middleware to builder' do 73 | expect(builder).to receive(:use).with(ConfigValidate) 74 | expect(builder).to receive(:use).with(ConnectOpenstack) 75 | expect(builder).to receive(:use).with(Call, ReadState) 76 | # TODO, Impove this test to check what's happen after ReadState 77 | Action.action_up 78 | end 79 | end 80 | 81 | describe 'action_halt' do 82 | it 'add others middleware to builder' do 83 | expect(builder).to receive(:use).with(ConfigValidate) 84 | expect(builder).to receive(:use).with(ConnectOpenstack) 85 | expect(builder).to receive(:use).with(Call, ReadState) 86 | # TODO, Impove this test to check what's happen after ReadState 87 | Action.action_halt 88 | end 89 | end 90 | 91 | describe 'action_suspend' do 92 | it 'add others middleware to builder' do 93 | expect(builder).to receive(:use).with(ConfigValidate) 94 | expect(builder).to receive(:use).with(ConnectOpenstack) 95 | expect(builder).to receive(:use).with(Call, ReadState) 96 | # TODO, Impove this test to check what's happen after ReadState 97 | Action.action_suspend 98 | end 99 | end 100 | 101 | describe 'action_resume' do 102 | it 'add others middleware to builder' do 103 | expect(builder).to receive(:use).with(ConfigValidate) 104 | expect(builder).to receive(:use).with(ConnectOpenstack) 105 | expect(builder).to receive(:use).with(Call, ReadState) 106 | # TODO, Impove this test to check what's happen after ReadState 107 | Action.action_resume 108 | end 109 | end 110 | 111 | describe 'action_reload' do 112 | it 'add others middleware to builder' do 113 | expect(builder).to receive(:use).with(ConfigValidate) 114 | expect(builder).to receive(:use).with(ConnectOpenstack) 115 | expect(builder).to receive(:use).with(Call, ReadState) 116 | # TODO, Impove this test to check what's happen after ReadState 117 | Action.action_reload 118 | end 119 | end 120 | end 121 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/client/glance_spec.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/spec_helper' 2 | 3 | describe VagrantPlugins::Openstack::GlanceClient do 4 | let(:http) do 5 | double('http').tap do |http| 6 | http.stub(:read_timeout) { 42 } 7 | http.stub(:open_timeout) { 43 } 8 | http.stub(:proxy) { nil } 9 | end 10 | end 11 | 12 | let(:config) do 13 | double('config').tap do |config| 14 | config.stub(:http) { http } 15 | config.stub(:ssl_ca_file) { nil } 16 | config.stub(:ssl_verify_peer) { true } 17 | end 18 | end 19 | 20 | let(:env) do 21 | {}.tap do |env| 22 | env[:machine] = double('machine') 23 | env[:machine].stub(:provider_config) { config } 24 | end 25 | end 26 | 27 | let(:session) do 28 | VagrantPlugins::Openstack.session 29 | end 30 | 31 | before :each do 32 | session.token = '123456' 33 | session.project_id = 'a1b2c3' 34 | session.endpoints = { image: 'http://glance' } 35 | @glance_client = VagrantPlugins::Openstack.glance 36 | end 37 | 38 | describe 'get_all_images' do 39 | context 'with token and project_id acquainted' do 40 | context 'and api version is v2' do 41 | it 'returns all images with details' do 42 | stub_request(:get, 'http://glance/images') 43 | .with( 44 | headers: 45 | { 46 | 'Accept' => 'application/json', 47 | 'X-Auth-Token' => '123456' 48 | }) 49 | .to_return( 50 | status: 200, 51 | body: ' 52 | { 53 | "images": [ 54 | { "id": "i1", "name": "image1", "visibility": "public", "size": "1024", "min_ram": "1", "min_disk": "10" }, 55 | { "id": "i2", "name": "image2", "visibility": "private", "size": "2048", "min_ram": "2", "min_disk": "20" } 56 | ] 57 | }') 58 | 59 | images = @glance_client.get_all_images(env) 60 | 61 | expect(images).to eq [Image.new('i1', 'image1', 'public', '1024', '1', '10'), 62 | Image.new('i2', 'image2', 'private', '2048', '2', '20')] 63 | end 64 | end 65 | 66 | context 'and api version is v1' do 67 | it 'returns all images with details' do 68 | stub_request(:get, 'http://glance/images') 69 | .with( 70 | headers: 71 | { 72 | 'Accept' => 'application/json', 73 | 'X-Auth-Token' => '123456' 74 | }) 75 | .to_return( 76 | status: 200, 77 | body: ' 78 | { 79 | "images": [ 80 | { "id": "i1", "name": "image1", "is_public": true }, 81 | { "id": "i2", "name": "image2", "is_public": false } 82 | ] 83 | }') 84 | 85 | stub_request(:get, 'http://glance/images/detail') 86 | .with( 87 | headers: 88 | { 89 | 'Accept' => 'application/json', 90 | 'X-Auth-Token' => '123456' 91 | }) 92 | .to_return( 93 | status: 200, 94 | body: ' 95 | { 96 | "images": [ 97 | { "id": "i1", "name": "image1", "is_public": true, "size": "1024", "min_ram": "1", "min_disk": "10" }, 98 | { "id": "i2", "name": "image2", "is_public": false, "size": "2048", "min_ram": "2", "min_disk": "20" } 99 | ] 100 | }') 101 | 102 | images = @glance_client.get_all_images(env) 103 | 104 | expect(images).to eq [Image.new('i1', 'image1', 'public', '1024', '1', '10'), 105 | Image.new('i2', 'image2', 'private', '2048', '2', '20')] 106 | end 107 | end 108 | end 109 | end 110 | 111 | describe 'get_api_version_list' do 112 | it 'returns version list' do 113 | stub_request(:get, 'http://glance/') 114 | .with(headers: { 'Accept' => 'application/json' }) 115 | .to_return( 116 | status: 200, 117 | body: '{ 118 | "versions": [ 119 | { 120 | "status": "...", 121 | "id": "v1.0", 122 | "links": [ 123 | { 124 | "href": "http://glance/v1.0", 125 | "rel": "self" 126 | } 127 | ] 128 | }, 129 | { 130 | "status": "CURRENT", 131 | "id": "v2.0", 132 | "links": [ 133 | { 134 | "href": "http://glance/v2.0", 135 | "rel": "self" 136 | } 137 | ] 138 | } 139 | ]}') 140 | 141 | versions = @glance_client.get_api_version_list(env) 142 | 143 | expect(versions.size).to eq(2) 144 | end 145 | end 146 | end 147 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/client/heat_spec.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/spec_helper' 2 | 3 | describe VagrantPlugins::Openstack::HeatClient do 4 | include FakeFS::SpecHelpers::All 5 | 6 | let(:http) do 7 | double('http').tap do |http| 8 | http.stub(:read_timeout) { 42 } 9 | http.stub(:open_timeout) { 43 } 10 | http.stub(:proxy) { nil } 11 | end 12 | end 13 | 14 | let(:config) do 15 | double('config').tap do |config| 16 | config.stub(:openstack_auth_url) { 'http://heatAuthV2' } 17 | config.stub(:openstack_orchestration_url) { nil } 18 | config.stub(:tenant_name) { 'testTenant' } 19 | config.stub(:username) { 'username' } 20 | config.stub(:password) { 'password' } 21 | config.stub(:http) { http } 22 | config.stub(:ssl_ca_file) { nil } 23 | config.stub(:ssl_verify_peer) { true } 24 | end 25 | end 26 | 27 | let(:env) do 28 | {}.tap do |env| 29 | env[:ui] = double('ui') 30 | env[:ui].stub(:info).with(anything) 31 | env[:machine] = double('machine') 32 | env[:machine].stub(:provider_config) { config } 33 | end 34 | end 35 | 36 | let(:session) do 37 | VagrantPlugins::Openstack.session 38 | end 39 | 40 | before :each do 41 | session.token = '123456' 42 | session.project_id = 'a1b2c3' 43 | session.endpoints = { orchestration: 'http://heat/a1b2c3' } 44 | @heat_client = VagrantPlugins::Openstack.heat 45 | end 46 | 47 | describe 'stack_exists' do 48 | context 'stack not found' do 49 | it 'raise an StackNotFound error' do 50 | stub_request(:get, 'http://heat/a1b2c3/stacks/stack_name/stack_id') 51 | .with( 52 | headers: 53 | { 54 | 'Accept' => 'application/json', 55 | 'X-Auth-Token' => '123456' 56 | }) 57 | .to_return( 58 | status: 404, 59 | body: '{"itemNotFound": {"message": "Stack could not be found", "code": 404}}') 60 | 61 | expect { @heat_client.get_stack_details(env, 'stack_name', 'stack_id') }.to raise_error(VagrantPlugins::Openstack::Errors::StackNotFound) 62 | end 63 | end 64 | end 65 | 66 | describe 'create_stack' do 67 | context 'with token and project_id acquainted' do 68 | it 'returns new stack id' do 69 | stub_request(:post, 'http://heat/a1b2c3/stacks') 70 | .with( 71 | body: '{"stack_name":"stck","template":"toto"}', 72 | headers: 73 | { 74 | 'Accept' => 'application/json', 75 | 'Content-Type' => 'application/json', 76 | 'X-Auth-Token' => '123456' 77 | }) 78 | .to_return(status: 202, body: '{ "stack": { "id": "o1o2o3" } }') 79 | 80 | stack_id = @heat_client.create_stack(env, name: 'stck', template: 'toto') 81 | 82 | expect(stack_id).to eq('o1o2o3') 83 | end 84 | end 85 | end 86 | 87 | describe 'get_stack_details' do 88 | context 'with token and project_id acquainted' do 89 | it 'returns stack details' do 90 | stub_request(:get, 'http://heat/a1b2c3/stacks/stack_id/stack_name') 91 | .with(headers: 92 | { 93 | 'Accept' => 'application/json', 94 | 'X-Auth-Token' => '123456' 95 | }) 96 | .to_return(status: 200, body: ' 97 | { 98 | "stack": { 99 | "description": "sample stack", 100 | "disable_rollback": "True", 101 | "id": "stack_id", 102 | "stack_name": "stack_name", 103 | "stack_status": "CREATE_COMPLETE" 104 | } 105 | }') 106 | 107 | stack = @heat_client.get_stack_details(env, 'stack_id', 'stack_name') 108 | 109 | expect(stack['id']).to eq('stack_id') 110 | expect(stack['stack_name']).to eq('stack_name') 111 | end 112 | end 113 | end 114 | 115 | describe 'delete_stack' do 116 | context 'with token and project_id acquainted' do 117 | it 'deletes the stack' do 118 | stub_request(:delete, 'http://heat/a1b2c3/stacks/stack_id/stack_name') 119 | .with(headers: 120 | { 121 | 'Accept' => 'application/json', 122 | 'X-Auth-Token' => '123456' 123 | }) 124 | .to_return(status: 204) 125 | 126 | @heat_client.delete_stack(env, 'stack_id', 'stack_name') 127 | end 128 | end 129 | end 130 | end 131 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/command/flavor_list_spec.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/spec_helper' 2 | 3 | describe VagrantPlugins::Openstack::Command::FlavorList do 4 | describe 'cmd' do 5 | let(:nova) do 6 | double('nova').tap do |nova| 7 | nova.stub(:get_all_flavors) do 8 | [ 9 | Flavor.new('001', 'small', '1', '1024', '10'), 10 | Flavor.new('002', 'large', '4', '4096', '100') 11 | ] 12 | end 13 | end 14 | end 15 | 16 | let(:env) do 17 | {}.tap do |env| 18 | env[:ui] = double('ui') 19 | env[:ui].stub(:info).with(anything) 20 | env[:openstack_client] = double 21 | env[:openstack_client].stub(:nova) { nova } 22 | end 23 | end 24 | 25 | before :each do 26 | @flavor_list_cmd = VagrantPlugins::Openstack::Command::FlavorList.new(nil, env) 27 | end 28 | 29 | it 'prints flovor list from server' do 30 | nova.should_receive(:get_all_flavors).with(env) 31 | 32 | expect(env[:ui]).to receive(:info).with(' 33 | +-----+-------+------+----------+----------------+ 34 | | ID | Name | vCPU | RAM (Mo) | Disk size (Go) | 35 | +-----+-------+------+----------+----------------+ 36 | | 001 | small | 1 | 1024 | 10 | 37 | | 002 | large | 4 | 4096 | 100 | 38 | +-----+-------+------+----------+----------------+') 39 | 40 | @flavor_list_cmd.cmd('flavor-list', [], env) 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/command/floatingip_list_spec.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/spec_helper' 2 | 3 | describe VagrantPlugins::Openstack::Command::FloatingIpList do 4 | describe 'cmd' do 5 | let(:nova) do 6 | double('nova').tap do |nova| 7 | nova.stub(:get_floating_ip_pools) do 8 | [ 9 | { 10 | 'name' => 'pool1' 11 | }, 12 | { 13 | 'name' => 'pool2' 14 | } 15 | ] 16 | end 17 | nova.stub(:get_floating_ips) do 18 | [ 19 | { 20 | 'fixed_ip' => nil, 21 | 'id' => 1, 22 | 'instance_id' => nil, 23 | 'ip' => '10.10.10.1', 24 | 'pool' => 'pool1' 25 | }, 26 | { 27 | 'fixed_ip' => nil, 28 | 'id' => 2, 29 | 'instance_id' => 'inst001', 30 | 'ip' => '10.10.10.2', 31 | 'pool' => 'pool2' 32 | } 33 | ] 34 | end 35 | end 36 | end 37 | 38 | let(:env) do 39 | {}.tap do |env| 40 | env[:ui] = double('ui') 41 | env[:ui].stub(:info).with(anything) 42 | env[:openstack_client] = double 43 | env[:openstack_client].stub(:nova) { nova } 44 | end 45 | end 46 | 47 | before :each do 48 | @floating_ip_list_cmd = VagrantPlugins::Openstack::Command::FloatingIpList.new(nil, env) 49 | end 50 | 51 | it 'prints floating ip and floating ip pool from server' do 52 | nova.should_receive(:get_floating_ip_pools).with(env) 53 | nova.should_receive(:get_floating_ips).with(env) 54 | 55 | expect(env[:ui]).to receive(:info).with(' 56 | +-------------------+ 57 | | Floating IP pools | 58 | +-------------------+ 59 | | pool1 | 60 | | pool2 | 61 | +-------------------+').ordered 62 | 63 | expect(env[:ui]).to receive(:info).with(' 64 | +----+------------+-------+-------------+ 65 | | ID | IP | Pool | Instance ID | 66 | +----+------------+-------+-------------+ 67 | | 1 | 10.10.10.1 | pool1 | | 68 | | 2 | 10.10.10.2 | pool2 | inst001 | 69 | +----+------------+-------+-------------+').ordered 70 | 71 | @floating_ip_list_cmd.cmd('floatingip-list', [], env) 72 | end 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/command/image_list_spec.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/spec_helper' 2 | 3 | describe VagrantPlugins::Openstack::Command::ImageList do 4 | let(:nova) do 5 | double('nova').tap do |nova| 6 | nova.stub(:get_all_images) do 7 | [ 8 | Image.new('0001', 'ubuntu'), 9 | Image.new('0002', 'centos'), 10 | Image.new('0003', 'debian') 11 | ] 12 | end 13 | end 14 | end 15 | 16 | let(:glance) do 17 | double('nova').tap do |nova| 18 | nova.stub(:get_all_images) do 19 | [ 20 | Image.new('0001', 'ubuntu', 'public', 700 * 1024 * 1024, 1, 10), 21 | Image.new('0002', 'centos', 'private', 800 * 1024 * 1024, 2, 20), 22 | Image.new('0003', 'debian', 'shared', 900 * 1024 * 1024, 4, 30) 23 | ] 24 | end 25 | end 26 | end 27 | 28 | let(:env) do 29 | {}.tap do |env| 30 | env[:ui] = double('ui') 31 | env[:ui].stub(:info).with(anything) 32 | env[:openstack_client] = double 33 | env[:openstack_client].stub(:nova) { nova } 34 | env[:openstack_client].stub(:glance) { glance } 35 | end 36 | end 37 | 38 | before :each do 39 | @image_list_cmd = VagrantPlugins::Openstack::Command::ImageList.new(['--'], env) 40 | end 41 | 42 | describe 'cmd' do 43 | context 'when glance is not available' do 44 | let(:session) do 45 | double('session').tap do |s| 46 | s.stub(:endpoints) { {} } 47 | end 48 | end 49 | 50 | it 'prints image list with only the id and the name' do 51 | env[:openstack_client].stub(:session) { session } 52 | allow(@image_list_cmd).to receive(:with_target_vms).and_return(nil) 53 | nova.should_receive(:get_all_images).with(env) 54 | 55 | expect(env[:ui]).to receive(:info).with(' 56 | +------+--------+ 57 | | ID | Name | 58 | +------+--------+ 59 | | 0001 | ubuntu | 60 | | 0002 | centos | 61 | | 0003 | debian | 62 | +------+--------+') 63 | @image_list_cmd.cmd('image-list', [], env) 64 | end 65 | end 66 | 67 | context 'when glance is available' do 68 | let(:session) do 69 | double('session').tap do |s| 70 | s.stub(:endpoints) do 71 | { 72 | image: 'http://glance' 73 | } 74 | end 75 | end 76 | end 77 | 78 | it 'prints image list with id, name and details' do 79 | env[:openstack_client].stub(:session) { session } 80 | allow(@image_list_cmd).to receive(:with_target_vms).and_return(nil) 81 | glance.should_receive(:get_all_images).with(env) 82 | 83 | expect(env[:ui]).to receive(:info).with(' 84 | +------+--------+------------+-----------+--------------+---------------+ 85 | | ID | Name | Visibility | Size (Mo) | Min RAM (Go) | Min Disk (Go) | 86 | +------+--------+------------+-----------+--------------+---------------+ 87 | | 0001 | ubuntu | public | 700 | 1 | 10 | 88 | | 0002 | centos | private | 800 | 2 | 20 | 89 | | 0003 | debian | shared | 900 | 4 | 30 | 90 | +------+--------+------------+-----------+--------------+---------------+') 91 | @image_list_cmd.cmd('image-list', [], env) 92 | end 93 | end 94 | end 95 | end 96 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/command/network_list_spec.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/spec_helper' 2 | 3 | describe VagrantPlugins::Openstack::Command::NetworkList do 4 | describe 'cmd' do 5 | let(:neutron) do 6 | double('neutron').tap do |neutron| 7 | neutron.stub(:get_private_networks) do 8 | [ 9 | Item.new('net-01', 'internal'), 10 | Item.new('net-02', 'external') 11 | ] 12 | end 13 | neutron.stub(:get_all_networks) do 14 | [ 15 | Item.new('pub-01', 'public'), 16 | Item.new('net-01', 'internal'), 17 | Item.new('net-02', 'external') 18 | ] 19 | end 20 | end 21 | end 22 | 23 | let(:env) do 24 | {}.tap do |env| 25 | env[:ui] = double('ui') 26 | env[:ui].stub(:info).with(anything) 27 | env[:openstack_client] = double 28 | env[:openstack_client].stub(:neutron) { neutron } 29 | end 30 | end 31 | 32 | before :each do 33 | @network_list_cmd = VagrantPlugins::Openstack::Command::NetworkList.new(nil, env) 34 | end 35 | 36 | it 'prints network list from server' do 37 | neutron.should_receive(:get_private_networks).with(env) 38 | 39 | expect(env[:ui]).to receive(:info).with(' 40 | +--------+----------+ 41 | | ID | Name | 42 | +--------+----------+ 43 | | net-01 | internal | 44 | | net-02 | external | 45 | +--------+----------+') 46 | 47 | @network_list_cmd.cmd('network-list', [], env) 48 | end 49 | 50 | it 'prints all networks list from server' do 51 | neutron.should_receive(:get_all_networks).with(env) 52 | 53 | expect(env[:ui]).to receive(:info).with(' 54 | +--------+----------+ 55 | | ID | Name | 56 | +--------+----------+ 57 | | pub-01 | public | 58 | | net-01 | internal | 59 | | net-02 | external | 60 | +--------+----------+') 61 | 62 | @network_list_cmd.cmd('network-list', ['all'], env) 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/command/reset_spec.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/spec_helper' 2 | 3 | describe VagrantPlugins::Openstack::Command::Reset do 4 | describe 'cmd' do 5 | let(:env) do 6 | {}.tap do |env| 7 | env[:ui] = double('ui') 8 | env[:ui].stub(:info).with(anything) 9 | env[:machine] = double('machine') 10 | env[:machine].stub(:data_dir) { '/my/data/dir' } 11 | end 12 | end 13 | 14 | before :each do 15 | @reset_cmd = VagrantPlugins::Openstack::Command::Reset.new(nil, env) 16 | end 17 | 18 | it 'resets vagrant openstack machines' do 19 | expect(env[:ui]).to receive(:info).with('Vagrant OpenStack Provider has been reset') 20 | expect(FileUtils).to receive(:remove_dir).with('/my/data/dir') 21 | @reset_cmd.cmd('reset', [], env) 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/command/subnet_list_spec.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/spec_helper' 2 | 3 | describe VagrantPlugins::Openstack::Command::SubnetList do 4 | describe 'cmd' do 5 | let(:neutron) do 6 | double('neutron').tap do |neutron| 7 | neutron.stub(:get_subnets) do 8 | [ 9 | Subnet.new('subnet-01', 'Subnet 1', '192.168.1.0/24', true, 'net-01'), 10 | Subnet.new('subnet-02', 'Subnet 2', '192.168.2.0/24', false, 'net-01'), 11 | Subnet.new('subnet-03', 'Subnet 3', '192.168.100.0/24', true, 'net-02') 12 | ] 13 | end 14 | end 15 | end 16 | 17 | let(:env) do 18 | {}.tap do |env| 19 | env[:ui] = double('ui') 20 | env[:ui].stub(:info).with(anything) 21 | env[:openstack_client] = double 22 | env[:openstack_client].stub(:neutron) { neutron } 23 | end 24 | end 25 | 26 | before :each do 27 | @subnet_list_cmd = VagrantPlugins::Openstack::Command::SubnetList.new(nil, env) 28 | end 29 | 30 | it 'prints subnet list from server' do 31 | neutron.should_receive(:get_subnets).with(env) 32 | 33 | expect(env[:ui]).to receive(:info).with(' 34 | +-----------+----------+------------------+-------+------------+ 35 | | ID | Name | CIDR | DHCP | Network ID | 36 | +-----------+----------+------------------+-------+------------+ 37 | | subnet-01 | Subnet 1 | 192.168.1.0/24 | true | net-01 | 38 | | subnet-02 | Subnet 2 | 192.168.2.0/24 | false | net-01 | 39 | | subnet-03 | Subnet 3 | 192.168.100.0/24 | true | net-02 | 40 | +-----------+----------+------------------+-------+------------+') 41 | 42 | @subnet_list_cmd.cmd('subnet-list', [], env) 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/command/volume_list_spec.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/spec_helper' 2 | 3 | describe VagrantPlugins::Openstack::Command::VolumeList do 4 | describe 'cmd' do 5 | let(:cinder) do 6 | double('cinder').tap do |cinder| 7 | cinder.stub(:get_all_volumes) do 8 | [Volume.new('987', 'vol-01', '2', 'available', 'true', nil, nil), 9 | Volume.new('654', 'vol-02', '4', 'in-use', 'false', 'inst-01', '/dev/vdc')] 10 | end 11 | end 12 | end 13 | 14 | let(:env) do 15 | {}.tap do |env| 16 | env[:ui] = double('ui') 17 | env[:ui].stub(:info).with(anything) 18 | env[:openstack_client] = double 19 | env[:openstack_client].stub(:cinder) { cinder } 20 | end 21 | end 22 | 23 | before :each do 24 | @volume_list_cmd = VagrantPlugins::Openstack::Command::VolumeList.new(nil, env) 25 | end 26 | 27 | it 'prints volumes list from server' do 28 | cinder.should_receive(:get_all_volumes).with(env) 29 | expect(env[:ui]).to receive(:info).with(' 30 | +-----+--------+-----------+-----------+-------------------------------------+ 31 | | ID | Name | Size (Go) | Status | Attachment (instance ID and device) | 32 | +-----+--------+-----------+-----------+-------------------------------------+ 33 | | 987 | vol-01 | 2 | available | | 34 | | 654 | vol-02 | 4 | in-use | inst-01 (/dev/vdc) | 35 | +-----+--------+-----------+-----------+-------------------------------------+') 36 | 37 | @volume_list_cmd.cmd('volume-list', [], env) 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/e2e_spec.rb.save: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/spec_helper' 2 | 3 | require 'aruba' 4 | require 'aruba/api' 5 | 6 | include Aruba::Api 7 | 8 | # spec/template_spec.rb 9 | require 'pathname' 10 | 11 | root = Pathname.new(__FILE__).parent.parent 12 | 13 | # Allows us to run commands directly, without worrying about the CWD 14 | ENV['PATH'] = "#{root.join('bin').to_s}#{File::PATH_SEPARATOR}#{ENV['PATH']}" 15 | 16 | describe "genud" do 17 | context "YAML templates" do 18 | it "should emit valid YAML to STDOUT", :focus do 19 | 20 | puts '#####################' 21 | # Run the command with Aruba's run_simple helper 22 | run_simple "bundle exec vagrant openstack", false, 10 23 | 24 | # assert_exit_status(0) 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/provider_spec.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/spec_helper' 2 | 3 | describe VagrantPlugins::Openstack::Provider do 4 | before :each do 5 | @provider = VagrantPlugins::Openstack::Provider.new :machine 6 | end 7 | 8 | describe 'to string' do 9 | it 'should give the provider name' do 10 | @provider.to_s.should eq('Openstack Cloud') 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/spec_helper.rb: -------------------------------------------------------------------------------- 1 | 2 | if ENV['COVERAGE'] != 'false' 3 | require 'simplecov' 4 | require 'coveralls' 5 | SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[ 6 | SimpleCov::Formatter::HTMLFormatter, 7 | Coveralls::SimpleCov::Formatter 8 | ] 9 | SimpleCov.start do 10 | add_filter 'spec' 11 | end 12 | end 13 | 14 | Dir[ 15 | 'lib/vagrant-openstack-provider/version_checker.rb', 16 | 'lib/vagrant-openstack-provider/logging.rb', 17 | 'lib/vagrant-openstack-provider/config.rb', 18 | 'lib/vagrant-openstack-provider/config_resolver.rb', 19 | 'lib/vagrant-openstack-provider/utils.rb', 20 | 'lib/vagrant-openstack-provider/errors.rb', 21 | 'lib/vagrant-openstack-provider/provider.rb', 22 | 'lib/vagrant-openstack-provider/client/*.rb', 23 | 'lib/vagrant-openstack-provider/command/*.rb', 24 | 'lib/vagrant-openstack-provider/action/*.rb'].each { |file| require file[4, file.length - 1] } 25 | 26 | require 'rspec/its' 27 | require 'webmock/rspec' 28 | require 'fakefs/safe' 29 | require 'fakefs/spec_helpers' 30 | 31 | RSpec.configure do |config| 32 | config.include FakeFS::SpecHelpers, fakefs: true 33 | end 34 | 35 | I18n.load_path << File.expand_path('locales/en.yml', Pathname.new(File.expand_path('../../../', __FILE__))) 36 | 37 | VagrantPlugins::Openstack::Logging.init 38 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/utils_spec.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/spec_helper' 2 | 3 | describe VagrantPlugins::Openstack::Utils do 4 | let(:config) do 5 | double('config').tap do |config| 6 | config.stub(:tenant_name) { 'testTenant' } 7 | config.stub(:server_name) { 'testName' } 8 | config.stub(:floating_ip) { nil } 9 | end 10 | end 11 | 12 | let(:nova) do 13 | double('nova').tap do |nova| 14 | nova.stub(:get_all_floating_ips).with(anything) do 15 | [FloatingIP.new('80.81.82.83', 'pool-1', nil), FloatingIP.new('30.31.32.33', 'pool-2', '1234')] 16 | end 17 | end 18 | end 19 | 20 | let(:env) do 21 | {}.tap do |env| 22 | env[:machine] = double('machine') 23 | env[:machine].stub(:provider_config) { config } 24 | env[:machine].stub(:id) { '1234id' } 25 | env[:openstack_client] = double('openstack_client') 26 | env[:openstack_client].stub(:nova) { nova } 27 | end 28 | end 29 | 30 | before :each do 31 | Utils.send(:public, *Utils.private_instance_methods) 32 | @action = Utils.new 33 | end 34 | 35 | describe 'get_ip_address' do 36 | context 'without config.floating_ip' do 37 | context 'with floating ip in nova details' do 38 | it 'returns floating_ip from nova details' do 39 | config.stub(:floating_ip) { nil } 40 | nova.stub(:get_server_details).with(env, '1234id') do 41 | { 42 | 'addresses' => { 43 | 'net' => [{ 44 | 'addr' => '13.13.13.13' 45 | }, { 46 | 'addr' => '12.12.12.12', 47 | 'OS-EXT-IPS:type' => 'floating' 48 | }] 49 | } 50 | } 51 | end 52 | @action.get_ip_address(env).should eq('12.12.12.12') 53 | end 54 | end 55 | 56 | context 'without floating ip in nova details' do 57 | context 'with a single ip in nova details' do 58 | it 'returns the single ip' do 59 | config.stub(:floating_ip) { nil } 60 | nova.stub(:get_server_details).with(env, '1234id') do 61 | { 62 | 'addresses' => { 63 | 'net' => [{ 64 | 'addr' => '13.13.13.13', 65 | 'OS-EXT-IPS:type' => 'fixed' 66 | }] 67 | } 68 | } 69 | end 70 | expect(@action.get_ip_address(env)).to eq('13.13.13.13') 71 | end 72 | end 73 | 74 | context 'with multiple ips in nova details' do 75 | it 'return the one corresponding to the first network in the Vagrantfile' do 76 | config.stub(:floating_ip) { nil } 77 | config.stub(:networks) { %w(net-2 net-1 net-3) } 78 | nova.stub(:get_server_details).with(env, '1234id') do 79 | { 80 | 'addresses' => { 81 | 'net-1' => [{ 82 | 'addr' => '11.11.11.11', 83 | 'OS-EXT-IPS:type' => 'fixed' 84 | }], 85 | 'net-2' => [{ 86 | 'addr' => '12.12.12.12', 87 | 'OS-EXT-IPS:type' => 'fixed' 88 | }], 89 | 'net-3' => [{ 90 | 'addr' => '13.13.13.13', 91 | 'OS-EXT-IPS:type' => 'fixed' 92 | }] 93 | } 94 | } 95 | end 96 | expect(@action.get_ip_address(env)).to eq('12.12.12.12') 97 | end 98 | end 99 | 100 | context 'with networks but no ips' do 101 | it 'fails' do 102 | config.stub(:floating_ip) { nil } 103 | nova.stub(:get_server_details).with(env, '1234id') do 104 | { 105 | 'addresses' => { 106 | 'net' => [] 107 | } 108 | } 109 | end 110 | expect { @action.get_ip_address(env) }.to raise_error(Errors::UnableToResolveIP) 111 | end 112 | end 113 | 114 | context 'with no networks ' do 115 | it 'fails' do 116 | config.stub(:floating_ip) { nil } 117 | nova.stub(:get_server_details).with(env, '1234id') do 118 | { 119 | 'addresses' => {} 120 | } 121 | end 122 | expect { @action.get_ip_address(env) }.to raise_error(Errors::UnableToResolveIP) 123 | end 124 | end 125 | end 126 | end 127 | end 128 | end 129 | -------------------------------------------------------------------------------- /source/spec/vagrant-openstack-provider/version_checker_spec.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-openstack-provider/spec_helper' 2 | 3 | describe VagrantPlugins::Openstack::VersionChecker do 4 | let(:version) do 5 | double('version') 6 | end 7 | 8 | before :each do 9 | @checker = VersionChecker.instance 10 | @checker.status = nil 11 | Gem.should_receive(:latest_spec_for) 12 | .with('vagrant-openstack-provider') 13 | .and_return(OpenStruct.new.tap { |v| v.version = version }) 14 | end 15 | 16 | describe 'check' do 17 | it { assert_version_is :latest, '1.2.3', '1.2.3' } 18 | it { assert_version_is :latest, '999.999.999', '999.999.999' } 19 | it { assert_version_is :unstable, '9999.999.999', '9999.999.999' } 20 | it { assert_version_is :unstable, '999.9999.999', '999.9999.999' } 21 | it { assert_version_is :unstable, '999.999.9999', '999.999.9999' } 22 | it { assert_version_is :unstable, '1.2', '1.2' } 23 | it { assert_version_is :outdated, '1.2.3', '1.2.2' } 24 | it { assert_version_is :outdated, '1.8.999', '1.8.998' } 25 | it { assert_version_is :outdated, '1.9.0', '1.8.999' } 26 | it { assert_version_is :outdated, '2.0.0', '1.999.999' } 27 | end 28 | 29 | private 30 | 31 | def assert_version_is(expected_status, latest, current) 32 | stub_const('VagrantPlugins::Openstack::VERSION', current) 33 | version.stub(:version) { latest } 34 | @checker.stub(:print_message) 35 | expect(@checker).to receive(:print_message) unless expected_status == :latest 36 | @checker.check 37 | expect(@checker.status).to eq(expected_status) 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /source/stackrc: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #Provider examples: 4 | #Numergy 5 | export OS_AUTH_URL=https://cloud.numergy.com/identity/v2.0/tokens 6 | export OS_NETWORK_URL=https://cloud.numergy.com/network/v2.0 7 | #.... 8 | 9 | if [ -z "$OS_USERNAME" ]; then 10 | echo "Please enter your OpenStack tenant name: " 11 | read -sr OS_TENANT_NAME_INPUT 12 | export OS_TENANT_NAME=$OS_TENANT_NAME_INPUT 13 | fi 14 | 15 | if [ -z "$OS_USERNAME" ]; then 16 | echo "Please enter your OpenStack username: " 17 | read -sr OS_USERNAME_INPUT 18 | export OS_USERNAME=$OS_USERNAME_INPUT 19 | fi 20 | 21 | if [ -z "$OS_PASSWORD" ]; then 22 | echo "Please enter your OpenStack Password: " 23 | read -sr OS_PASSWORD_INPUT 24 | export OS_PASSWORD=$OS_PASSWORD_INPUT 25 | fi 26 | -------------------------------------------------------------------------------- /source/vagrant-openstack-provider.gemspec: -------------------------------------------------------------------------------- 1 | lib = File.expand_path('../lib', __FILE__) 2 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 3 | require 'vagrant-openstack-provider/version' 4 | 5 | Gem::Specification.new do |gem| 6 | gem.name = 'vagrant-openstack-provider' 7 | gem.version = VagrantPlugins::Openstack::VERSION 8 | gem.authors = ['Guillaume Giamarchi', 'Julien Vey'] 9 | gem.email = ['guillaume.giamarchi@gmail.com', 'vey.julien@gmail.com'] 10 | gem.description = 'Enables Vagrant to manage machines in OpenStack Cloud.' 11 | gem.summary = 'Enables Vagrant to manage machines in OpenStack Cloud.' 12 | gem.homepage = 'https://github.com/ggiamarchi/vagrant-openstack-provider' 13 | gem.license = 'MIT' 14 | 15 | gem.add_dependency 'json', '> 2', '< 3' 16 | gem.add_dependency 'rest-client', '>= 1.6.0', '< 3.0' 17 | gem.add_dependency 'terminal-table', '1.4.5' 18 | gem.add_dependency 'sshkey', '1.6.1' 19 | gem.add_dependency 'colorize', '0.7.3' 20 | gem.add_dependency 'public_suffix', '2.0.5' 21 | 22 | # Constraint rake to properly handle deprecated method usage 23 | # from within rspec 3.1.z 24 | gem.add_development_dependency 'rake', '~> 11.3.0' 25 | gem.add_development_dependency 'rspec', '~> 3.1.0' 26 | gem.add_development_dependency 'rspec-its', '~> 1.0.1' 27 | gem.add_development_dependency 'rspec-expectations', '~> 3.1.2' 28 | gem.add_development_dependency 'webmock', '~> 3.4.2' 29 | gem.add_development_dependency 'fakefs', '~> 0.5.2' 30 | 31 | gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR) 32 | gem.executables = gem.files.grep(/^bin\//).map { |f| File.basename(f) } 33 | gem.test_files = gem.files.grep(/^(test|spec|features)\//) 34 | gem.require_paths = ['lib'] 35 | end 36 | --------------------------------------------------------------------------------