vagrant-openstack-provider 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +12 -5
  3. data/CHANGELOG.md +18 -0
  4. data/Gemfile +3 -2
  5. data/{LICENSE.txt → LICENSE} +2 -1
  6. data/lib/vagrant-openstack-provider/action.rb +3 -2
  7. data/lib/vagrant-openstack-provider/action/provision.rb +60 -0
  8. data/lib/vagrant-openstack-provider/action/read_ssh_info.rb +4 -0
  9. data/lib/vagrant-openstack-provider/client/domain.rb +2 -2
  10. data/lib/vagrant-openstack-provider/client/keystone.rb +17 -6
  11. data/lib/vagrant-openstack-provider/client/nova.rb +14 -3
  12. data/lib/vagrant-openstack-provider/command/abstract_command.rb +1 -0
  13. data/lib/vagrant-openstack-provider/command/main.rb +1 -3
  14. data/lib/vagrant-openstack-provider/config.rb +3 -3
  15. data/lib/vagrant-openstack-provider/config_resolver.rb +46 -16
  16. data/lib/vagrant-openstack-provider/errors.rb +15 -0
  17. data/lib/vagrant-openstack-provider/plugin.rb +7 -1
  18. data/lib/vagrant-openstack-provider/version.rb +11 -1
  19. data/lib/vagrant-openstack-provider/version_checker.rb +76 -0
  20. data/locales/en.yml +21 -4
  21. data/spec/vagrant-openstack-provider/action/connect_openstack_spec.rb +17 -19
  22. data/spec/vagrant-openstack-provider/action/create_server_spec.rb +19 -18
  23. data/spec/vagrant-openstack-provider/action/create_stack_spec.rb +4 -6
  24. data/spec/vagrant-openstack-provider/action/delete_server_spec.rb +4 -6
  25. data/spec/vagrant-openstack-provider/action/delete_stack_spec.rb +1 -2
  26. data/spec/vagrant-openstack-provider/action/message_spec.rb +1 -2
  27. data/spec/vagrant-openstack-provider/action/provision_spec.rb +104 -0
  28. data/spec/vagrant-openstack-provider/action/read_ssh_info_spec.rb +1 -3
  29. data/spec/vagrant-openstack-provider/action/read_state_spec.rb +1 -2
  30. data/spec/vagrant-openstack-provider/action/resume_server_spec.rb +1 -2
  31. data/spec/vagrant-openstack-provider/action/start_server_spec.rb +1 -2
  32. data/spec/vagrant-openstack-provider/action/stop_server_spec.rb +1 -2
  33. data/spec/vagrant-openstack-provider/action/suspend_server_spec.rb +1 -2
  34. data/spec/vagrant-openstack-provider/action/sync_folders_spec.rb +1 -2
  35. data/spec/vagrant-openstack-provider/action/wait_accessible_spec.rb +1 -2
  36. data/spec/vagrant-openstack-provider/action/wait_active_spec.rb +3 -4
  37. data/spec/vagrant-openstack-provider/action/wait_stop_spec.rb +3 -4
  38. data/spec/vagrant-openstack-provider/action_spec.rb +0 -1
  39. data/spec/vagrant-openstack-provider/client/cinder_spec.rb +5 -8
  40. data/spec/vagrant-openstack-provider/client/glance_spec.rb +69 -70
  41. data/spec/vagrant-openstack-provider/client/heat_spec.rb +24 -28
  42. data/spec/vagrant-openstack-provider/client/keystone_spec.rb +34 -16
  43. data/spec/vagrant-openstack-provider/client/neutron_spec.rb +76 -80
  44. data/spec/vagrant-openstack-provider/client/nova_spec.rb +198 -168
  45. data/spec/vagrant-openstack-provider/client/utils_spec.rb +1 -3
  46. data/spec/vagrant-openstack-provider/command/flavor_list_spec.rb +1 -2
  47. data/spec/vagrant-openstack-provider/command/floatingip_list_spec.rb +1 -2
  48. data/spec/vagrant-openstack-provider/command/image_list_spec.rb +1 -6
  49. data/spec/vagrant-openstack-provider/command/network_list_spec.rb +1 -3
  50. data/spec/vagrant-openstack-provider/command/reset_spec.rb +1 -2
  51. data/spec/vagrant-openstack-provider/command/subnet_list_spec.rb +1 -2
  52. data/spec/vagrant-openstack-provider/command/volume_list_spec.rb +1 -2
  53. data/spec/vagrant-openstack-provider/config_resolver_spec.rb +100 -6
  54. data/spec/vagrant-openstack-provider/config_spec.rb +2 -6
  55. data/spec/vagrant-openstack-provider/e2e_spec.rb.save +27 -0
  56. data/spec/vagrant-openstack-provider/spec_helper.rb +1 -0
  57. data/spec/vagrant-openstack-provider/utils_spec.rb +1 -2
  58. data/spec/vagrant-openstack-provider/version_checker_spec.rb +39 -0
  59. data/vagrant-openstack-provider.gemspec +4 -2
  60. metadata +29 -9
  61. data/gemfiles/latest_stable.gemfile +0 -10
  62. data/gemfiles/minimal_release.gemfile +0 -10
  63. data/gemfiles/previous_release.gemfile +0 -10
@@ -22,7 +22,7 @@ describe VagrantPlugins::Openstack::NovaClient do
22
22
  end
23
23
 
24
24
  let(:env) do
25
- Hash.new.tap do |env|
25
+ {}.tap do |env|
26
26
  env[:ui] = double('ui')
27
27
  env[:ui].stub(:info).with(anything)
28
28
  env[:machine] = double('machine')
@@ -45,19 +45,18 @@ describe VagrantPlugins::Openstack::NovaClient do
45
45
  context 'stack not found' do
46
46
  it 'raise an StackNotFound error' do
47
47
  stub_request(:get, 'http://heat/a1b2c3/stacks/stack_name/stack_id')
48
- .with(
49
- headers:
50
- {
51
- 'Accept' => 'application/json',
52
- 'Accept-Encoding' => 'gzip, deflate',
53
- 'X-Auth-Token' => '123456'
54
- })
55
- .to_return(
56
- status: 404,
57
- body: '{"itemNotFound": {"message": "Stack could not be found", "code": 404}}')
48
+ .with(
49
+ headers:
50
+ {
51
+ 'Accept' => 'application/json',
52
+ 'Accept-Encoding' => 'gzip, deflate',
53
+ 'X-Auth-Token' => '123456'
54
+ })
55
+ .to_return(
56
+ status: 404,
57
+ body: '{"itemNotFound": {"message": "Stack could not be found", "code": 404}}')
58
58
 
59
59
  expect { @heat_client.get_stack_details(env, 'stack_name', 'stack_id') }.to raise_error(VagrantPlugins::Openstack::Errors::StackNotFound)
60
-
61
60
  end
62
61
  end
63
62
  end
@@ -65,17 +64,16 @@ describe VagrantPlugins::Openstack::NovaClient do
65
64
  describe 'create_stack' do
66
65
  context 'with token and project_id acquainted' do
67
66
  it 'returns new stack id' do
68
-
69
67
  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" } }')
68
+ .with(
69
+ body: '{"stack_name":"stck","template":"toto"}',
70
+ headers:
71
+ {
72
+ 'Accept' => 'application/json',
73
+ 'Content-Type' => 'application/json',
74
+ 'X-Auth-Token' => '123456'
75
+ })
76
+ .to_return(status: 202, body: '{ "stack": { "id": "o1o2o3" } }')
79
77
 
80
78
  stack_id = @heat_client.create_stack(env, name: 'stck', template: 'toto')
81
79
 
@@ -87,14 +85,13 @@ describe VagrantPlugins::Openstack::NovaClient do
87
85
  describe 'get_stack_details' do
88
86
  context 'with token and project_id acquainted' do
89
87
  it 'returns stack details' do
90
-
91
88
  stub_request(:get, 'http://heat/a1b2c3/stacks/stack_id/stack_name')
92
- .with(headers:
89
+ .with(headers:
93
90
  {
94
91
  'Accept' => 'application/json',
95
92
  'X-Auth-Token' => '123456'
96
93
  })
97
- .to_return(status: 200, body: '
94
+ .to_return(status: 200, body: '
98
95
  {
99
96
  "stack": {
100
97
  "description": "sample stack",
@@ -116,14 +113,13 @@ describe VagrantPlugins::Openstack::NovaClient do
116
113
  describe 'delete_stack' do
117
114
  context 'with token and project_id acquainted' do
118
115
  it 'deletes the stack' do
119
-
120
116
  stub_request(:delete, 'http://heat/a1b2c3/stacks/stack_id/stack_name')
121
- .with(headers:
117
+ .with(headers:
122
118
  {
123
119
  'Accept' => 'application/json',
124
120
  'X-Auth-Token' => '123456'
125
121
  })
126
- .to_return(status: 204)
122
+ .to_return(status: 204)
127
123
 
128
124
  @heat_client.delete_stack(env, 'stack_id', 'stack_name')
129
125
  end
@@ -1,7 +1,6 @@
1
1
  require 'vagrant-openstack-provider/spec_helper'
2
2
 
3
3
  describe VagrantPlugins::Openstack::KeystoneClient do
4
-
5
4
  let(:http) do
6
5
  double('http').tap do |http|
7
6
  http.stub(:read_timeout) { 42 }
@@ -11,7 +10,7 @@ describe VagrantPlugins::Openstack::KeystoneClient do
11
10
 
12
11
  let(:config) do
13
12
  double('config').tap do |config|
14
- config.stub(:openstack_auth_url) { 'http://keystoneAuthV2' }
13
+ config.stub(:openstack_auth_url) { 'http://keystoneAuthV2/tokens' }
15
14
  config.stub(:openstack_compute_url) { nil }
16
15
  config.stub(:openstack_network_url) { nil }
17
16
  config.stub(:tenant_name) { 'testTenant' }
@@ -22,7 +21,7 @@ describe VagrantPlugins::Openstack::KeystoneClient do
22
21
  end
23
22
 
24
23
  let(:env) do
25
- Hash.new.tap do |env|
24
+ {}.tap do |env|
26
25
  env[:ui] = double('ui')
27
26
  env[:ui].stub(:info).with(anything)
28
27
  env[:machine] = double('machine')
@@ -35,7 +34,6 @@ describe VagrantPlugins::Openstack::KeystoneClient do
35
34
  end
36
35
 
37
36
  describe 'authenticate' do
38
-
39
37
  let(:keystone_request_headers) do
40
38
  {
41
39
  'Accept' => 'application/json',
@@ -60,11 +58,11 @@ describe VagrantPlugins::Openstack::KeystoneClient do
60
58
 
61
59
  context 'with good credentials' do
62
60
  it 'store token and tenant id' do
63
- stub_request(:post, 'http://keystoneAuthV2')
64
- .with(
61
+ stub_request(:post, 'http://keystoneAuthV2/tokens')
62
+ .with(
65
63
  body: keystone_request_body,
66
64
  headers: keystone_request_headers)
67
- .to_return(
65
+ .to_return(
68
66
  status: 200,
69
67
  body: keystone_response_body,
70
68
  headers: keystone_request_headers)
@@ -78,11 +76,11 @@ describe VagrantPlugins::Openstack::KeystoneClient do
78
76
 
79
77
  context 'with wrong credentials' do
80
78
  it 'raise an unauthorized error' do
81
- stub_request(:post, 'http://keystoneAuthV2')
82
- .with(
79
+ stub_request(:post, 'http://keystoneAuthV2/tokens')
80
+ .with(
83
81
  body: keystone_request_body,
84
82
  headers: keystone_request_headers)
85
- .to_return(
83
+ .to_return(
86
84
  status: 401,
87
85
  body: '{
88
86
  "error": {
@@ -99,24 +97,44 @@ describe VagrantPlugins::Openstack::KeystoneClient do
99
97
 
100
98
  context 'with bad endpoint' do
101
99
  it 'raise a BadAuthenticationEndpoint error' do
102
- stub_request(:post, 'http://keystoneAuthV2')
103
- .with(
100
+ stub_request(:post, 'http://keystoneAuthV2/tokens')
101
+ .with(
104
102
  body: keystone_request_body,
105
103
  headers: keystone_request_headers)
106
- .to_return(
104
+ .to_return(
107
105
  status: 404)
108
106
 
109
107
  expect { @keystone_client.authenticate(env) }.to raise_error(Errors::BadAuthenticationEndpoint)
110
108
  end
111
109
  end
112
110
 
111
+ context 'with /tokens suffix missing in URL' do
112
+ it 'raise add the suffix' do
113
+ config.stub(:openstack_auth_url) { 'http://keystoneAuthV2' }
114
+
115
+ stub_request(:post, 'http://keystoneAuthV2/tokens')
116
+ .with(
117
+ body: keystone_request_body,
118
+ headers: keystone_request_headers)
119
+ .to_return(
120
+ status: 200,
121
+ body: keystone_response_body,
122
+ headers: keystone_request_headers)
123
+
124
+ @keystone_client.authenticate(env)
125
+
126
+ session.token.should eq('0123456789')
127
+ session.project_id.should eq('testTenantId')
128
+ end
129
+ end
130
+
113
131
  context 'with internal server error' do
114
132
  it 'raise a VagrantOpenstackError error with response body as message' do
115
- stub_request(:post, 'http://keystoneAuthV2')
116
- .with(
133
+ stub_request(:post, 'http://keystoneAuthV2/tokens')
134
+ .with(
117
135
  body: keystone_request_body,
118
136
  headers: keystone_request_headers)
119
- .to_return(
137
+ .to_return(
120
138
  status: 500,
121
139
  body: 'Internal server error')
122
140
 
@@ -1,7 +1,6 @@
1
1
  require 'vagrant-openstack-provider/spec_helper'
2
2
 
3
3
  describe VagrantPlugins::Openstack::NeutronClient do
4
-
5
4
  let(:http) do
6
5
  double('http').tap do |http|
7
6
  http.stub(:read_timeout) { 42 }
@@ -16,7 +15,7 @@ describe VagrantPlugins::Openstack::NeutronClient do
16
15
  end
17
16
 
18
17
  let(:env) do
19
- Hash.new.tap do |env|
18
+ {}.tap do |env|
20
19
  env[:machine] = double('machine')
21
20
  env[:machine].stub(:provider_config) { config }
22
21
  end
@@ -36,25 +35,24 @@ describe VagrantPlugins::Openstack::NeutronClient do
36
35
  describe 'get_private_networks' do
37
36
  context 'with token' do
38
37
  it 'returns only private networks for project in session' do
39
-
40
38
  stub_request(:get, 'http://neutron/networks')
41
- .with(
42
- headers:
39
+ .with(
40
+ headers:
41
+ {
42
+ 'Accept' => 'application/json',
43
+ 'X-Auth-Token' => '123456'
44
+ })
45
+ .to_return(
46
+ status: 200,
47
+ body: '
43
48
  {
44
- 'Accept' => 'application/json',
45
- 'X-Auth-Token' => '123456'
46
- })
47
- .to_return(
48
- status: 200,
49
- body: '
50
- {
51
- "networks": [
52
- { "name": "PublicNetwork", "tenant_id": "admin-tenant-id", "id": "net-pub" },
53
- { "name": "net1", "tenant_id": "a1b2c3", "id": "net-1" },
54
- { "name": "net2", "tenant_id": "a1b2c3", "id": "net-2" }
55
- ]
56
- }
57
- ')
49
+ "networks": [
50
+ { "name": "PublicNetwork", "tenant_id": "admin-tenant-id", "id": "net-pub" },
51
+ { "name": "net1", "tenant_id": "a1b2c3", "id": "net-1" },
52
+ { "name": "net2", "tenant_id": "a1b2c3", "id": "net-2" }
53
+ ]
54
+ }
55
+ ')
58
56
 
59
57
  networks = @neutron_client.get_private_networks(env)
60
58
 
@@ -70,25 +68,24 @@ describe VagrantPlugins::Openstack::NeutronClient do
70
68
  describe 'get_all_networks' do
71
69
  context 'with token' do
72
70
  it 'returns all networks for project in session' do
73
-
74
71
  stub_request(:get, 'http://neutron/networks')
75
- .with(
76
- headers:
72
+ .with(
73
+ headers:
74
+ {
75
+ 'Accept' => 'application/json',
76
+ 'X-Auth-Token' => '123456'
77
+ })
78
+ .to_return(
79
+ status: 200,
80
+ body: '
77
81
  {
78
- 'Accept' => 'application/json',
79
- 'X-Auth-Token' => '123456'
80
- })
81
- .to_return(
82
- status: 200,
83
- body: '
84
- {
85
- "networks": [
86
- { "name": "PublicNetwork", "tenant_id": "admin-tenant-id", "id": "net-pub" },
87
- { "name": "net1", "tenant_id": "a1b2c3", "id": "net-1" },
88
- { "name": "net2", "tenant_id": "a1b2c3", "id": "net-2" }
89
- ]
90
- }
91
- ')
82
+ "networks": [
83
+ { "name": "PublicNetwork", "tenant_id": "admin-tenant-id", "id": "net-pub" },
84
+ { "name": "net1", "tenant_id": "a1b2c3", "id": "net-1" },
85
+ { "name": "net2", "tenant_id": "a1b2c3", "id": "net-2" }
86
+ ]
87
+ }
88
+ ')
92
89
 
93
90
  networks = @neutron_client.get_all_networks(env)
94
91
 
@@ -106,25 +103,24 @@ describe VagrantPlugins::Openstack::NeutronClient do
106
103
  describe 'get_subnets' do
107
104
  context 'with token' do
108
105
  it 'returns all available subnets' do
109
-
110
106
  stub_request(:get, 'http://neutron/subnets')
111
- .with(
112
- headers:
113
- {
114
- 'Accept' => 'application/json',
115
- 'X-Auth-Token' => '123456'
116
- })
117
- .to_return(
118
- status: 200,
119
- body: '
120
- {
121
- "subnets": [
122
- { "id": "subnet-01", "name": "Subnet 1", "cidr": "192.168.1.0/24", "enable_dhcp": true, "network_id": "net-01" },
123
- { "id": "subnet-02", "name": "Subnet 2", "cidr": "192.168.2.0/24", "enable_dhcp": false, "network_id": "net-01" },
124
- { "id": "subnet-03", "name": "Subnet 3", "cidr": "192.168.100.0/24", "enable_dhcp": true, "network_id": "net-02" }
125
- ]
126
- }
127
- ')
107
+ .with(
108
+ headers:
109
+ {
110
+ 'Accept' => 'application/json',
111
+ 'X-Auth-Token' => '123456'
112
+ })
113
+ .to_return(
114
+ status: 200,
115
+ body: '
116
+ {
117
+ "subnets": [
118
+ { "id": "subnet-01", "name": "Subnet 1", "cidr": "192.168.1.0/24", "enable_dhcp": true, "network_id": "net-01" },
119
+ { "id": "subnet-02", "name": "Subnet 2", "cidr": "192.168.2.0/24", "enable_dhcp": false, "network_id": "net-01" },
120
+ { "id": "subnet-03", "name": "Subnet 3", "cidr": "192.168.100.0/24", "enable_dhcp": true, "network_id": "net-02" }
121
+ ]
122
+ }
123
+ ')
128
124
 
129
125
  networks = @neutron_client.get_subnets(env)
130
126
 
@@ -139,32 +135,32 @@ describe VagrantPlugins::Openstack::NeutronClient do
139
135
  context 'basic' do
140
136
  it 'returns version list' do
141
137
  stub_request(:get, 'http://neutron/')
142
- .with(header: { 'Accept' => 'application/json' })
143
- .to_return(
144
- status: 200,
145
- body: '{
146
- "versions": [
147
- {
148
- "status": "...",
149
- "id": "v1.0",
150
- "links": [
151
- {
152
- "href": "http://neutron/v1.0",
153
- "rel": "self"
154
- }
155
- ]
156
- },
157
- {
158
- "status": "CURRENT",
159
- "id": "v2.0",
160
- "links": [
161
- {
162
- "href": "http://neutron/v2.0",
163
- "rel": "self"
164
- }
165
- ]
166
- }
167
- ]}')
138
+ .with(header: { 'Accept' => 'application/json' })
139
+ .to_return(
140
+ status: 200,
141
+ body: '{
142
+ "versions": [
143
+ {
144
+ "status": "...",
145
+ "id": "v1.0",
146
+ "links": [
147
+ {
148
+ "href": "http://neutron/v1.0",
149
+ "rel": "self"
150
+ }
151
+ ]
152
+ },
153
+ {
154
+ "status": "CURRENT",
155
+ "id": "v2.0",
156
+ "links": [
157
+ {
158
+ "href": "http://neutron/v2.0",
159
+ "rel": "self"
160
+ }
161
+ ]
162
+ }
163
+ ]}')
168
164
 
169
165
  versions = @neutron_client.get_api_version_list(env, :network)
170
166
 
@@ -31,7 +31,7 @@ describe VagrantPlugins::Openstack::NovaClient do
31
31
  end
32
32
 
33
33
  let(:env) do
34
- Hash.new.tap do |env|
34
+ {}.tap do |env|
35
35
  env[:ui] = double('ui')
36
36
  env[:ui].stub(:info).with(anything)
37
37
  env[:machine] = double('machine')
@@ -54,20 +54,19 @@ describe VagrantPlugins::Openstack::NovaClient do
54
54
  context 'instance not found' do
55
55
  it 'raise an InstanceNotFound error' do
56
56
  stub_request(:post, 'http://nova/a1b2c3/servers/o1o2o3/action')
57
- .with(
58
- body: '{"os-start":null}',
59
- headers:
60
- {
61
- 'Accept' => 'application/json',
62
- 'Content-Type' => 'application/json',
63
- 'X-Auth-Token' => '123456'
64
- })
65
- .to_return(
66
- status: 404,
67
- body: '{"itemNotFound": {"message": "Instance could not be found", "code": 404}}')
57
+ .with(
58
+ body: '{"os-start":null}',
59
+ headers:
60
+ {
61
+ 'Accept' => 'application/json',
62
+ 'Content-Type' => 'application/json',
63
+ 'X-Auth-Token' => '123456'
64
+ })
65
+ .to_return(
66
+ status: 404,
67
+ body: '{"itemNotFound": {"message": "Instance could not be found", "code": 404}}')
68
68
 
69
69
  expect { @nova_client.start_server(env, 'o1o2o3') }.to raise_error(VagrantPlugins::Openstack::Errors::InstanceNotFound)
70
-
71
70
  end
72
71
  end
73
72
  end
@@ -76,19 +75,19 @@ describe VagrantPlugins::Openstack::NovaClient do
76
75
  context 'with token and project_id acquainted' do
77
76
  it 'returns all flavors' do
78
77
  stub_request(:get, 'http://nova/a1b2c3/flavors/detail')
79
- .with(
80
- headers:
81
- {
82
- 'Accept' => 'application/json',
83
- 'X-Auth-Token' => '123456'
84
- })
85
- .to_return(
86
- status: 200,
87
- body: '{
88
- "flavors": [
89
- { "id": "f1", "name": "flavor1", "vcpus":"1", "ram": "1024", "disk": "10"},
90
- { "id": "f2", "name": "flavor2", "vcpus":"2", "ram": "2048", "disk": "20"}
91
- ]}')
78
+ .with(
79
+ headers:
80
+ {
81
+ 'Accept' => 'application/json',
82
+ 'X-Auth-Token' => '123456'
83
+ })
84
+ .to_return(
85
+ status: 200,
86
+ body: '{
87
+ "flavors": [
88
+ { "id": "f1", "name": "flavor1", "vcpus":"1", "ram": "1024", "disk": "10"},
89
+ { "id": "f2", "name": "flavor2", "vcpus":"2", "ram": "2048", "disk": "20"}
90
+ ]}')
92
91
 
93
92
  flavors = @nova_client.get_all_flavors(env)
94
93
 
@@ -103,15 +102,15 @@ describe VagrantPlugins::Openstack::NovaClient do
103
102
  context 'with token and project_id acquainted' do
104
103
  it 'returns all images' do
105
104
  stub_request(:get, 'http://nova/a1b2c3/images')
106
- .with(
107
- headers:
108
- {
109
- 'Accept' => 'application/json',
110
- 'X-Auth-Token' => '123456'
111
- })
112
- .to_return(
113
- status: 200,
114
- body: '{ "images": [ { "id": "i1", "name": "image1"}, { "id": "i2", "name": "image2"} ] }')
105
+ .with(
106
+ headers:
107
+ {
108
+ 'Accept' => 'application/json',
109
+ 'X-Auth-Token' => '123456'
110
+ })
111
+ .to_return(
112
+ status: 200,
113
+ body: '{ "images": [ { "id": "i1", "name": "image1"}, { "id": "i2", "name": "image2"} ] }')
115
114
 
116
115
  images = @nova_client.get_all_images(env)
117
116
 
@@ -127,17 +126,16 @@ describe VagrantPlugins::Openstack::NovaClient do
127
126
  describe 'create_server' do
128
127
  context 'with token and project_id acquainted' do
129
128
  it 'returns new instance id' do
130
-
131
129
  stub_request(:post, 'http://nova/a1b2c3/servers')
132
- .with(
133
- body: '{"server":{"name":"inst","imageRef":"img","flavorRef":"flav","key_name":"key"}}',
134
- headers:
135
- {
136
- 'Accept' => 'application/json',
137
- 'Content-Type' => 'application/json',
138
- 'X-Auth-Token' => '123456'
139
- })
140
- .to_return(status: 202, body: '{ "server": { "id": "o1o2o3" } }')
130
+ .with(
131
+ body: '{"server":{"name":"inst","imageRef":"img","flavorRef":"flav","key_name":"key"}}',
132
+ headers:
133
+ {
134
+ 'Accept' => 'application/json',
135
+ 'Content-Type' => 'application/json',
136
+ 'X-Auth-Token' => '123456'
137
+ })
138
+ .to_return(status: 202, body: '{ "server": { "id": "o1o2o3" } }')
141
139
 
142
140
  instance_id = @nova_client.create_server(env, name: 'inst', image_ref: 'img', flavor_ref: 'flav', networks: nil, keypair: 'key')
143
141
 
@@ -147,17 +145,17 @@ describe VagrantPlugins::Openstack::NovaClient do
147
145
  context 'with all options specified' do
148
146
  it 'returns new instance id' do
149
147
  stub_request(:post, 'http://nova/a1b2c3/servers')
150
- .with(
151
- body: '{"server":{"name":"inst","imageRef":"img","flavorRef":"flav","key_name":"key",'\
152
- '"security_groups":[{"name":"default"}],"user_data":"dXNlcl9kYXRhX3Rlc3Q=\n","metadata":"metadata_test"},'\
153
- '"scheduler_hints":"sched_hints_test"}',
154
- headers:
155
- {
156
- 'Accept' => 'application/json',
157
- 'Content-Type' => 'application/json',
158
- 'X-Auth-Token' => '123456'
159
- })
160
- .to_return(status: 202, body: '{ "server": { "id": "o1o2o3" } }')
148
+ .with(
149
+ body: '{"server":{"name":"inst","imageRef":"img","flavorRef":"flav","key_name":"key",'\
150
+ '"security_groups":[{"name":"default"}],"user_data":"dXNlcl9kYXRhX3Rlc3Q=\n","metadata":"metadata_test"},'\
151
+ '"os:scheduler_hints":"sched_hints_test"}',
152
+ headers:
153
+ {
154
+ 'Accept' => 'application/json',
155
+ 'Content-Type' => 'application/json',
156
+ 'X-Auth-Token' => '123456'
157
+ })
158
+ .to_return(status: 202, body: '{ "server": { "id": "o1o2o3" } }')
161
159
 
162
160
  instance_id = @nova_client.create_server(
163
161
  env,
@@ -177,9 +175,8 @@ describe VagrantPlugins::Openstack::NovaClient do
177
175
 
178
176
  context 'with two networks' do
179
177
  it 'returns new instance id' do
180
-
181
178
  stub_request(:post, 'http://nova/a1b2c3/servers')
182
- .with(
179
+ .with(
183
180
  body: '{"server":{"name":"inst","imageRef":"img","flavorRef":"flav","key_name":"key","networks":[{"uuid":"net1"},{"uuid":"net2"}]}}',
184
181
  headers:
185
182
  {
@@ -187,7 +184,7 @@ describe VagrantPlugins::Openstack::NovaClient do
187
184
  'Content-Type' => 'application/json',
188
185
  'X-Auth-Token' => '123456'
189
186
  })
190
- .to_return(status: 202, body: '{ "server": { "id": "o1o2o3" } }')
187
+ .to_return(status: 202, body: '{ "server": { "id": "o1o2o3" } }')
191
188
 
192
189
  instance_id = @nova_client.create_server(env, name: 'inst', image_ref: 'img', flavor_ref: 'flav',
193
190
  networks: [{ uuid: 'net1' }, { uuid: 'net2' }], keypair: 'key')
@@ -198,9 +195,8 @@ describe VagrantPlugins::Openstack::NovaClient do
198
195
 
199
196
  context 'with availability_zone' do
200
197
  it 'returns new instance id' do
201
-
202
198
  stub_request(:post, 'http://nova/a1b2c3/servers')
203
- .with(
199
+ .with(
204
200
  body: '{"server":{"name":"inst","imageRef":"img","flavorRef":"flav","key_name":"key","availability_zone":"avz"}}',
205
201
  headers:
206
202
  {
@@ -208,7 +204,7 @@ describe VagrantPlugins::Openstack::NovaClient do
208
204
  'Content-Type' => 'application/json',
209
205
  'X-Auth-Token' => '123456'
210
206
  })
211
- .to_return(status: 202, body: '{ "server": { "id": "o1o2o3" } }')
207
+ .to_return(status: 202, body: '{ "server": { "id": "o1o2o3" } }')
212
208
 
213
209
  instance_id = @nova_client.create_server(env, name: 'inst', image_ref: 'img', flavor_ref: 'flav', keypair: 'key', availability_zone: 'avz')
214
210
 
@@ -216,22 +212,51 @@ describe VagrantPlugins::Openstack::NovaClient do
216
212
  end
217
213
  end
218
214
 
219
- context 'with volume_boot' do
220
- it 'returns new instance id' do
221
-
215
+ context 'with volume_boot creating volume' do
216
+ it 'create bootable volume and returns new instance id' do
222
217
  stub_request(:post, 'http://nova/a1b2c3/servers')
223
- .with(
224
- body: '{"server":{"name":"inst","block_device_mapping":[{"volume_id":"vol","device_name":"vda"}],"flavorRef":"flav","key_name":"key"}}',
218
+ .with(
219
+ body: '{"server":{"name":"inst","block_device_mapping_v2":[{"boot_index":"0","volume_size":"10","uuid":"image_id",'\
220
+ '"device_name":"vda","source_type":"image","destination_type":"volume","delete_on_termination":"false"}],'\
221
+ '"flavorRef":"flav","key_name":"key"}}',
225
222
  headers:
226
- {
227
- 'Accept' => 'application/json',
228
- 'Content-Type' => 'application/json',
229
- 'X-Auth-Token' => '123456'
230
- })
231
- .to_return(status: 202, body: '{ "server": { "id": "o1o2o3" } }')
223
+ {
224
+ 'Accept' => 'application/json',
225
+ 'Content-Type' => 'application/json',
226
+ 'X-Auth-Token' => '123456'
227
+ })
228
+ .to_return(status: 202, body: '{ "server": { "id": "o1o2o3" } }')
229
+
230
+ instance_id = @nova_client.create_server(env,
231
+ name: 'inst',
232
+ image_ref: nil,
233
+ volume_boot: { image: 'image_id', device: 'vda', size: '10',
234
+ delete_on_destroy: 'false' },
235
+ flavor_ref: 'flav',
236
+ keypair: 'key')
237
+ expect(instance_id).to eq('o1o2o3')
238
+ end
239
+ end
232
240
 
233
- instance_id = @nova_client.create_server(env, name: 'inst', volume_boot: { id: 'vol', device: 'vda' }, flavor_ref: 'flav', keypair: 'key')
241
+ context 'with volume_boot id' do
242
+ it 'returns new instance id' do
243
+ stub_request(:post, 'http://nova/a1b2c3/servers')
244
+ .with(
245
+ body: '{"server":{"name":"inst","block_device_mapping":[{"volume_id":"vol","device_name":"vda"}],'\
246
+ '"flavorRef":"flav","key_name":"key"}}',
247
+ headers:
248
+ {
249
+ 'Accept' => 'application/json',
250
+ 'Content-Type' => 'application/json',
251
+ 'X-Auth-Token' => '123456'
252
+ })
253
+ .to_return(status: 202, body: '{ "server": { "id": "o1o2o3" } }')
234
254
 
255
+ instance_id = @nova_client.create_server(env,
256
+ name: 'inst',
257
+ volume_boot: { id: 'vol', device: 'vda' },
258
+ flavor_ref: 'flav',
259
+ keypair: 'key')
235
260
  expect(instance_id).to eq('o1o2o3')
236
261
  end
237
262
  end
@@ -241,17 +266,15 @@ describe VagrantPlugins::Openstack::NovaClient do
241
266
  describe 'delete_server' do
242
267
  context 'with token and project_id acquainted' do
243
268
  it 'returns new instance id' do
244
-
245
269
  stub_request(:delete, 'http://nova/a1b2c3/servers/o1o2o3')
246
- .with(
247
- headers: {
248
- 'Accept' => 'application/json',
249
- 'X-Auth-Token' => '123456'
250
- })
251
- .to_return(status: 204)
270
+ .with(
271
+ headers: {
272
+ 'Accept' => 'application/json',
273
+ 'X-Auth-Token' => '123456'
274
+ })
275
+ .to_return(status: 204)
252
276
 
253
277
  @nova_client.delete_server(env, 'o1o2o3')
254
-
255
278
  end
256
279
  end
257
280
  end
@@ -264,13 +287,13 @@ describe VagrantPlugins::Openstack::NovaClient do
264
287
  Kernel.stub(:rand).and_return(2_036_069_739_008)
265
288
 
266
289
  stub_request(:post, 'http://nova/a1b2c3/os-keypairs')
267
- .with(
268
- body: "{\"keypair\":{\"name\":\"vagrant-generated-pzcvcpa8\",\"public_key\":\"#{ssh_key_content}\"}}",
269
- headers: {
270
- 'Accept' => 'application/json',
271
- 'Content-Type' => 'application/json',
272
- 'X-Auth-Token' => '123456' })
273
- .to_return(status: 200, body: '
290
+ .with(
291
+ body: "{\"keypair\":{\"name\":\"vagrant-generated-pzcvcpa8\",\"public_key\":\"#{ssh_key_content}\"}}",
292
+ headers: {
293
+ 'Accept' => 'application/json',
294
+ 'Content-Type' => 'application/json',
295
+ 'X-Auth-Token' => '123456' })
296
+ .to_return(status: 200, body: '
274
297
  {
275
298
  "keypair": {
276
299
  "name": "created_key_name"
@@ -278,7 +301,6 @@ describe VagrantPlugins::Openstack::NovaClient do
278
301
  }')
279
302
 
280
303
  @nova_client.import_keypair_from_file(env, filename)
281
-
282
304
  end
283
305
  end
284
306
  end
@@ -288,12 +310,12 @@ describe VagrantPlugins::Openstack::NovaClient do
288
310
  context 'with keypair not generated by vagrant' do
289
311
  it 'do nothing' do
290
312
  stub_request(:get, 'http://nova/a1b2c3/servers/o1o2o3')
291
- .with(headers:
313
+ .with(headers:
292
314
  {
293
315
  'Accept' => 'application/json',
294
316
  'X-Auth-Token' => '123456'
295
317
  })
296
- .to_return(status: 200, body: '
318
+ .to_return(status: 200, body: '
297
319
  {
298
320
  "server": {
299
321
  "id": "o1o2o3",
@@ -308,15 +330,15 @@ describe VagrantPlugins::Openstack::NovaClient do
308
330
  context 'with keypair generated by vagrant' do
309
331
  it 'deletes the key on nova' do
310
332
  stub_request(:delete, 'http://nova/a1b2c3/os-keypairs/vagrant-generated-1234')
311
- .with(headers: { 'X-Auth-Token' => '123456' })
312
- .to_return(status: 202)
333
+ .with(headers: { 'X-Auth-Token' => '123456' })
334
+ .to_return(status: 202)
313
335
  stub_request(:get, 'http://nova/a1b2c3/servers/o1o2o3')
314
- .with(headers:
336
+ .with(headers:
315
337
  {
316
338
  'Accept' => 'application/json',
317
339
  'X-Auth-Token' => '123456'
318
340
  })
319
- .to_return(status: 200, body: '
341
+ .to_return(status: 200, body: '
320
342
  {
321
343
  "server": {
322
344
  "id": "o1o2o3",
@@ -328,23 +350,44 @@ describe VagrantPlugins::Openstack::NovaClient do
328
350
  @nova_client.delete_keypair_if_vagrant(env, 'o1o2o3')
329
351
  end
330
352
  end
353
+ context 'with keypair generated by vagrant and missing in server details' do
354
+ it 'do nothing' do
355
+ stub_request(:delete, 'http://nova/a1b2c3/os-keypairs/vagrant-generated-1234')
356
+ .with(headers: { 'X-Auth-Token' => '123456' })
357
+ .to_return(status: 202)
358
+ stub_request(:get, 'http://nova/a1b2c3/servers/o1o2o3')
359
+ .with(headers:
360
+ {
361
+ 'Accept' => 'application/json',
362
+ 'X-Auth-Token' => '123456'
363
+ })
364
+ .to_return(status: 200, body: '
365
+ {
366
+ "server": {
367
+ "id": "o1o2o3"
368
+ }
369
+ }
370
+ ')
371
+
372
+ @nova_client.delete_keypair_if_vagrant(env, 'o1o2o3')
373
+ end
374
+ end
331
375
  end
332
376
  end
333
377
 
334
378
  describe 'suspend_server' do
335
379
  context 'with token and project_id acquainted' do
336
380
  it 'returns new instance id' do
337
-
338
381
  stub_request(:post, 'http://nova/a1b2c3/servers/o1o2o3/action')
339
- .with(
340
- body: '{"suspend":null}',
341
- headers:
342
- {
343
- 'Accept' => 'application/json',
344
- 'Content-Type' => 'application/json',
345
- 'X-Auth-Token' => '123456'
346
- })
347
- .to_return(status: 202)
382
+ .with(
383
+ body: '{"suspend":null}',
384
+ headers:
385
+ {
386
+ 'Accept' => 'application/json',
387
+ 'Content-Type' => 'application/json',
388
+ 'X-Auth-Token' => '123456'
389
+ })
390
+ .to_return(status: 202)
348
391
 
349
392
  @nova_client.suspend_server(env, 'o1o2o3')
350
393
  end
@@ -354,17 +397,16 @@ describe VagrantPlugins::Openstack::NovaClient do
354
397
  describe 'resume_server' do
355
398
  context 'with token and project_id acquainted' do
356
399
  it 'returns new instance id' do
357
-
358
400
  stub_request(:post, 'http://nova/a1b2c3/servers/o1o2o3/action')
359
- .with(
360
- body: '{"resume":null}',
361
- headers:
362
- {
363
- 'Accept' => 'application/json',
364
- 'Content-Type' => 'application/json',
365
- 'X-Auth-Token' => '123456'
366
- })
367
- .to_return(status: 202)
401
+ .with(
402
+ body: '{"resume":null}',
403
+ headers:
404
+ {
405
+ 'Accept' => 'application/json',
406
+ 'Content-Type' => 'application/json',
407
+ 'X-Auth-Token' => '123456'
408
+ })
409
+ .to_return(status: 202)
368
410
 
369
411
  @nova_client.resume_server(env, 'o1o2o3')
370
412
  end
@@ -374,20 +416,18 @@ describe VagrantPlugins::Openstack::NovaClient do
374
416
  describe 'stop_server' do
375
417
  context 'with token and project_id acquainted' do
376
418
  it 'returns new instance id' do
377
-
378
419
  stub_request(:post, 'http://nova/a1b2c3/servers/o1o2o3/action')
379
- .with(
380
- body: '{"os-stop":null}',
381
- headers:
382
- {
383
- 'Accept' => 'application/json',
384
- 'Content-Type' => 'application/json',
385
- 'X-Auth-Token' => '123456'
386
- })
387
- .to_return(status: 202)
420
+ .with(
421
+ body: '{"os-stop":null}',
422
+ headers:
423
+ {
424
+ 'Accept' => 'application/json',
425
+ 'Content-Type' => 'application/json',
426
+ 'X-Auth-Token' => '123456'
427
+ })
428
+ .to_return(status: 202)
388
429
 
389
430
  @nova_client.stop_server(env, 'o1o2o3')
390
-
391
431
  end
392
432
  end
393
433
  end
@@ -395,20 +435,18 @@ describe VagrantPlugins::Openstack::NovaClient do
395
435
  describe 'start_server' do
396
436
  context 'with token and project_id acquainted' do
397
437
  it 'returns new instance id' do
398
-
399
438
  stub_request(:post, 'http://nova/a1b2c3/servers/o1o2o3/action')
400
- .with(
401
- body: '{"os-start":null}',
402
- headers:
403
- {
404
- 'Accept' => 'application/json',
405
- 'Content-Type' => 'application/json',
406
- 'X-Auth-Token' => '123456'
407
- })
408
- .to_return(status: 202)
439
+ .with(
440
+ body: '{"os-start":null}',
441
+ headers:
442
+ {
443
+ 'Accept' => 'application/json',
444
+ 'Content-Type' => 'application/json',
445
+ 'X-Auth-Token' => '123456'
446
+ })
447
+ .to_return(status: 202)
409
448
 
410
449
  @nova_client.start_server(env, 'o1o2o3')
411
-
412
450
  end
413
451
  end
414
452
  end
@@ -417,14 +455,14 @@ describe VagrantPlugins::Openstack::NovaClient do
417
455
  context 'with token and project_id acquainted' do
418
456
  it 'returns all floating ips' do
419
457
  stub_request(:get, 'http://nova/a1b2c3/os-floating-ips')
420
- .with(headers:
458
+ .with(headers:
421
459
  {
422
460
  'Accept' => 'application/json',
423
461
  'Accept-Encoding' => 'gzip, deflate',
424
462
  'User-Agent' => 'Ruby',
425
463
  'X-Auth-Token' => '123456'
426
464
  })
427
- .to_return(status: 200, body: '
465
+ .to_return(status: 200, body: '
428
466
  {
429
467
  "floating_ips": [
430
468
  {"instance_id": "1234",
@@ -460,12 +498,12 @@ describe VagrantPlugins::Openstack::NovaClient do
460
498
  context 'with token and project_id acquainted' do
461
499
  it 'return newly allocated floating_ip' do
462
500
  stub_request(:post, 'http://nova/a1b2c3/os-floating-ips')
463
- .with(body: '{"pool":"pool-1"}',
464
- headers: {
465
- 'Accept' => 'application/json',
466
- 'Content-Type' => 'application/json',
467
- 'X-Auth-Token' => '123456' })
468
- .to_return(status: 200, body: '
501
+ .with(body: '{"pool":"pool-1"}',
502
+ headers: {
503
+ 'Accept' => 'application/json',
504
+ 'Content-Type' => 'application/json',
505
+ 'X-Auth-Token' => '123456' })
506
+ .to_return(status: 200, body: '
469
507
  {
470
508
  "floating_ip": {
471
509
  "instance_id": null,
@@ -487,14 +525,13 @@ describe VagrantPlugins::Openstack::NovaClient do
487
525
  describe 'get_server_details' do
488
526
  context 'with token and project_id acquainted' do
489
527
  it 'returns server details' do
490
-
491
528
  stub_request(:get, 'http://nova/a1b2c3/servers/o1o2o3')
492
- .with(headers:
529
+ .with(headers:
493
530
  {
494
531
  'Accept' => 'application/json',
495
532
  'X-Auth-Token' => '123456'
496
533
  })
497
- .to_return(status: 200, body: '
534
+ .to_return(status: 200, body: '
498
535
  {
499
536
  "server": {
500
537
  "addresses": { "private": [ { "addr": "192.168.0.3", "version": 4 } ] },
@@ -519,23 +556,20 @@ describe VagrantPlugins::Openstack::NovaClient do
519
556
  expect(server['tenant_id']).to eq('openstack')
520
557
  expect(server['image']['id']).to eq('i1')
521
558
  expect(server['flavor']['id']).to eq('1')
522
-
523
559
  end
524
560
  end
525
561
  end
526
562
 
527
563
  describe 'add_floating_ip' do
528
-
529
564
  context 'with token and project_id acquainted and IP available' do
530
565
  it 'returns server details' do
531
-
532
566
  stub_request(:get, 'http://nova/a1b2c3/os-floating-ips')
533
- .with(headers:
567
+ .with(headers:
534
568
  {
535
569
  'Accept' => 'application/json',
536
570
  'X-Auth-Token' => '123456'
537
571
  })
538
- .to_return(status: 200, body: '
572
+ .to_return(status: 200, body: '
539
573
  {
540
574
  "floating_ips": [
541
575
  {
@@ -556,14 +590,14 @@ describe VagrantPlugins::Openstack::NovaClient do
556
590
  }')
557
591
 
558
592
  stub_request(:post, 'http://nova/a1b2c3/servers/o1o2o3/action')
559
- .with(body: '{"addFloatingIp":{"address":"1.2.3.4"}}',
560
- headers:
593
+ .with(body: '{"addFloatingIp":{"address":"1.2.3.4"}}',
594
+ headers:
561
595
  {
562
596
  'Accept' => 'application/json',
563
597
  'Content-Type' => 'application/json',
564
598
  'X-Auth-Token' => '123456'
565
599
  })
566
- .to_return(status: 202)
600
+ .to_return(status: 202)
567
601
 
568
602
  @nova_client.add_floating_ip(env, 'o1o2o3', '1.2.3.4')
569
603
  end
@@ -571,14 +605,13 @@ describe VagrantPlugins::Openstack::NovaClient do
571
605
 
572
606
  context 'with token and project_id acquainted and IP already in use' do
573
607
  it 'raise an error' do
574
-
575
608
  stub_request(:get, 'http://nova/a1b2c3/os-floating-ips')
576
- .with(headers:
609
+ .with(headers:
577
610
  {
578
611
  'Accept' => 'application/json',
579
612
  'X-Auth-Token' => '123456'
580
613
  })
581
- .to_return(status: 200, body: '
614
+ .to_return(status: 200, body: '
582
615
  {
583
616
  "floating_ips": [
584
617
  {
@@ -604,14 +637,13 @@ describe VagrantPlugins::Openstack::NovaClient do
604
637
 
605
638
  context 'with token and project_id acquainted and IP not allocated' do
606
639
  it 'raise an error' do
607
-
608
640
  stub_request(:get, 'http://nova/a1b2c3/os-floating-ips')
609
- .with(headers:
641
+ .with(headers:
610
642
  {
611
643
  'Accept' => 'application/json',
612
644
  'X-Auth-Token' => '123456'
613
645
  })
614
- .to_return(status: 200, body: '
646
+ .to_return(status: 200, body: '
615
647
  {
616
648
  "floating_ips": [
617
649
  {
@@ -632,7 +664,6 @@ describe VagrantPlugins::Openstack::NovaClient do
632
664
  describe 'get_floating_ip_pools' do
633
665
  context 'with token and project_id acquainted' do
634
666
  it 'should return floating ip pool' do
635
-
636
667
  stub_request(:get, 'http://nova/a1b2c3/os-floating-ip-pools')
637
668
  .with(headers: { 'Accept' => 'application/json', 'X-Auth-Token' => '123456' })
638
669
  .to_return(status: 200, body: '
@@ -659,7 +690,6 @@ describe VagrantPlugins::Openstack::NovaClient do
659
690
  describe 'get_floating_ips' do
660
691
  context 'with token and project_id acquainted' do
661
692
  it 'should return floating ip list' do
662
-
663
693
  stub_request(:get, 'http://nova/a1b2c3/os-floating-ips')
664
694
  .with(headers: { 'Accept' => 'application/json', 'X-Auth-Token' => '123456' })
665
695
  .to_return(status: 200, body: '
@@ -701,13 +731,13 @@ describe VagrantPlugins::Openstack::NovaClient do
701
731
  context 'with volume id and device' do
702
732
  it 'call the nova api' do
703
733
  stub_request(:post, 'http://nova/a1b2c3/servers/9876/os-volume_attachments')
704
- .with(headers:
734
+ .with(headers:
705
735
  {
706
736
  'Accept' => 'application/json',
707
737
  'X-Auth-Token' => '123456'
708
738
  },
709
- body: '{"volumeAttachment":{"volumeId":"n1n2","device":"/dev/vdg"}}')
710
- .to_return(status: 200, body: '
739
+ body: '{"volumeAttachment":{"volumeId":"n1n2","device":"/dev/vdg"}}')
740
+ .to_return(status: 200, body: '
711
741
  {
712
742
  "volumeAttachment": {
713
743
  "device": "/dev/vdg",