knife-openstack 0.10.0 → 1.0.0.rc1
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.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +20 -22
- data/Gemfile +11 -1
- data/README.md +6 -4
- data/Rakefile +33 -0
- data/knife-openstack.gemspec +7 -8
- data/lib/chef/knife/cloud/openstack_server_create_options.rb +75 -0
- data/lib/chef/knife/cloud/openstack_service.rb +62 -0
- data/lib/chef/knife/cloud/openstack_service_options.rb +50 -0
- data/lib/chef/knife/openstack_flavor_list.rb +37 -51
- data/lib/chef/knife/openstack_group_list.rb +38 -45
- data/lib/chef/knife/openstack_helpers.rb +30 -0
- data/lib/chef/knife/openstack_image_list.rb +42 -59
- data/lib/chef/knife/openstack_network_list.rb +25 -21
- data/lib/chef/knife/openstack_server_create.rb +166 -452
- data/lib/chef/knife/openstack_server_delete.rb +26 -106
- data/lib/chef/knife/openstack_server_list.rb +37 -59
- data/lib/chef/knife/openstack_server_show.rb +57 -0
- data/lib/knife-openstack/version.rb +1 -1
- data/spec/functional/flavor_list_func_spec.rb +45 -0
- data/spec/functional/group_list_func_spec.rb +67 -0
- data/spec/functional/image_list_func_spec.rb +51 -0
- data/spec/functional/network_list_func_spec.rb +44 -0
- data/spec/functional/server_create_func_spec.rb +118 -0
- data/spec/functional/server_delete_func_spec.rb +84 -0
- data/spec/functional/server_list_func_spec.rb +95 -0
- data/spec/functional/server_show_func_spec.rb +46 -0
- data/spec/integration/cleanup.rb +91 -0
- data/spec/integration/config/environment.yml.sample +13 -0
- data/spec/integration/openstack_spec.rb +618 -0
- data/spec/spec_helper.rb +126 -0
- data/spec/unit/openstack_flavor_list_spec.rb +30 -0
- data/spec/unit/openstack_group_list_spec.rb +43 -0
- data/spec/unit/openstack_image_list_spec.rb +32 -0
- data/spec/unit/openstack_network_list_spec.rb +39 -0
- data/spec/unit/openstack_server_create_spec.rb +344 -182
- data/spec/unit/openstack_server_delete_spec.rb +43 -0
- data/spec/unit/openstack_server_list_spec.rb +32 -0
- data/spec/unit/openstack_server_show_spec.rb +42 -0
- data/spec/unit/openstack_service_spec.rb +89 -0
- data/spec/unit/validate_spec.rb +55 -0
- metadata +95 -51
- data/lib/chef/knife/openstack_base.rb +0 -182
@@ -1,57 +1,50 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
# License:: Apache License, Version 2.0
|
5
|
-
#
|
6
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
-
# you may not use this file except in compliance with the License.
|
8
|
-
# You may obtain a copy of the License at
|
9
|
-
#
|
10
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
-
#
|
12
|
-
# Unless required by applicable law or agreed to in writing, software
|
13
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
-
# See the License for the specific language governing permissions and
|
16
|
-
# limitations under the License.
|
17
|
-
#
|
18
|
-
|
19
|
-
require 'chef/knife/openstack_base'
|
1
|
+
require 'chef/knife/cloud/list_resource_command'
|
2
|
+
require 'chef/knife/openstack_helpers'
|
3
|
+
require 'chef/knife/cloud/openstack_service_options'
|
20
4
|
|
21
5
|
class Chef
|
22
6
|
class Knife
|
23
|
-
class
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
banner "knife openstack group list (options)"
|
7
|
+
class Cloud
|
8
|
+
class OpenstackGroupList < ResourceListCommand
|
9
|
+
include OpenstackHelpers
|
10
|
+
include OpenstackServiceOptions
|
28
11
|
|
29
|
-
|
12
|
+
banner "knife openstack group list (options)"
|
30
13
|
|
31
|
-
|
14
|
+
def query_resource
|
15
|
+
begin
|
16
|
+
@service.connection.security_groups
|
17
|
+
rescue Excon::Errors::BadRequest => e
|
18
|
+
response = Chef::JSONCompat.from_json(e.response.body)
|
19
|
+
ui.fatal("Unknown server error (#{response['badRequest']['code']}): #{response['badRequest']['message']}")
|
20
|
+
raise e
|
21
|
+
end
|
22
|
+
end
|
32
23
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
24
|
+
def list(security_groups)
|
25
|
+
group_list = [
|
26
|
+
ui.color('Name', :bold),
|
27
|
+
ui.color('Protocol', :bold),
|
28
|
+
ui.color('From', :bold),
|
29
|
+
ui.color('To', :bold),
|
30
|
+
ui.color('CIDR', :bold),
|
31
|
+
ui.color('Description', :bold),
|
32
|
+
]
|
33
|
+
security_groups.sort_by(&:name).each do |group|
|
34
|
+
group.security_group_rules.each do |rule|
|
35
|
+
unless rule.ip_protocol.nil?
|
36
|
+
group_list << group.name
|
37
|
+
group_list << rule.ip_protocol
|
38
|
+
group_list << rule.from_port.to_s
|
39
|
+
group_list << rule.to_port.to_s
|
40
|
+
group_list << rule.ip_range['cidr']
|
41
|
+
group_list << group.description
|
42
|
+
end
|
50
43
|
end
|
51
44
|
end
|
45
|
+
puts ui.list(group_list, :uneven_columns_across, 6)
|
52
46
|
end
|
53
|
-
puts ui.list(group_list, :uneven_columns_across, 6)
|
54
47
|
end
|
55
48
|
end
|
56
49
|
end
|
57
|
-
end
|
50
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'chef/knife/cloud/openstack_service_options'
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class Cloud
|
6
|
+
module OpenstackHelpers
|
7
|
+
|
8
|
+
def primary_private_ip_address(addresses)
|
9
|
+
primary_network_ip_address(addresses, 'private')
|
10
|
+
end
|
11
|
+
|
12
|
+
def primary_public_ip_address(addresses)
|
13
|
+
primary_network_ip_address(addresses, 'public')
|
14
|
+
end
|
15
|
+
|
16
|
+
def primary_network_ip_address(addresses, network_name)
|
17
|
+
return addresses[network_name].last['addr'] if addresses[network_name] && !addresses[network_name].empty?
|
18
|
+
end
|
19
|
+
|
20
|
+
def create_service_instance
|
21
|
+
OpenstackService.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def validate!
|
25
|
+
super(:openstack_username, :openstack_password, :openstack_auth_url)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -1,71 +1,54 @@
|
|
1
|
-
#
|
2
|
-
#
|
3
|
-
# Author:: Matt Ray (<matt@getchef.com>)
|
4
|
-
# Copyright:: Copyright (c) 2011-2014 Chef Software, Inc.
|
5
|
-
# License:: Apache License, Version 2.0
|
6
|
-
#
|
7
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
-
# you may not use this file except in compliance with the License.
|
9
|
-
# You may obtain a copy of the License at
|
10
|
-
#
|
11
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
-
#
|
13
|
-
# Unless required by applicable law or agreed to in writing, software
|
14
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
-
# See the License for the specific language governing permissions and
|
17
|
-
# limitations under the License.
|
18
|
-
#
|
1
|
+
# Author:: Prabhu Das (<prabhu.das@clogeny.com>)
|
2
|
+
# Copyright:: Copyright (c) 2014 Chef Software, Inc.
|
19
3
|
|
20
|
-
require 'chef/knife/
|
4
|
+
require 'chef/knife/cloud/list_resource_command'
|
5
|
+
require 'chef/knife/openstack_helpers'
|
6
|
+
require 'chef/knife/cloud/openstack_service_options'
|
21
7
|
|
22
8
|
class Chef
|
23
9
|
class Knife
|
24
|
-
class
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
10
|
+
class Cloud
|
11
|
+
class OpenstackImageList < ResourceListCommand
|
12
|
+
include OpenstackHelpers
|
13
|
+
include OpenstackServiceOptions
|
14
|
+
|
15
|
+
banner "knife openstack image list (options)"
|
16
|
+
|
17
|
+
option :disable_filter,
|
18
|
+
:long => "--disable-filter",
|
19
|
+
:description => "Disable filtering of the image list. Currently filters names ending with 'initrd' or 'kernel'",
|
20
|
+
:boolean => true,
|
21
|
+
:default => false
|
22
|
+
|
23
|
+
def before_exec_command
|
24
|
+
#set resource_filters
|
25
|
+
if !config[:disable_filter]
|
26
|
+
@resource_filters = [{:attribute => 'name', :regex => /initrd$|kernel$|loader$|virtual$|vmlinuz$/}]
|
27
|
+
end
|
28
|
+
#set columns_with_info map
|
29
|
+
@columns_with_info = [
|
30
|
+
{:label => 'Name', :key => 'name'},
|
31
|
+
{:label => 'ID', :key => 'id'},
|
32
|
+
{:label => 'Snapshot', :key => 'metadata', :value_callback => method(:is_image_snapshot)}
|
33
|
+
]
|
34
|
+
@sort_by_field = "name"
|
35
|
+
end
|
37
36
|
|
38
|
-
|
37
|
+
def query_resource
|
38
|
+
@service.list_images
|
39
|
+
end
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
begin
|
46
|
-
connection.images.sort_by do |image|
|
47
|
-
[image.name.to_s.downcase, image.id].compact
|
48
|
-
end.each do |image|
|
49
|
-
unless ((image.name =~ /initrd$|kernel$|loader$|virtual$|vmlinuz$/) &&
|
50
|
-
!config[:disable_filter])
|
51
|
-
image_list << image.name
|
52
|
-
image_list << image.id
|
53
|
-
snapshot = 'no'
|
54
|
-
image.metadata.each do |datum|
|
55
|
-
if (datum.key == 'image_type') && (datum.value == 'snapshot')
|
56
|
-
snapshot = 'yes'
|
57
|
-
end
|
58
|
-
end
|
59
|
-
image_list << snapshot
|
41
|
+
def is_image_snapshot(metadata)
|
42
|
+
snapshot = 'no'
|
43
|
+
metadata.each do |datum|
|
44
|
+
if (datum.key == 'image_type') && (datum.value == 'snapshot')
|
45
|
+
snapshot = 'yes'
|
60
46
|
end
|
61
47
|
end
|
62
|
-
|
63
|
-
response = Chef::JSONCompat.from_json(e.response.body)
|
64
|
-
ui.fatal("Unknown server error (#{response['badRequest']['code']}): #{response['badRequest']['message']}")
|
65
|
-
raise e
|
48
|
+
snapshot
|
66
49
|
end
|
67
|
-
|
50
|
+
|
68
51
|
end
|
69
52
|
end
|
70
53
|
end
|
71
|
-
end
|
54
|
+
end
|
@@ -1,31 +1,35 @@
|
|
1
|
-
|
1
|
+
# Author:: Prabhu Das (<prabhu.das@clogeny.com>)
|
2
|
+
# Copyright:: Copyright (c) 2014 Chef Software, Inc.
|
3
|
+
|
4
|
+
require 'chef/knife/cloud/list_resource_command'
|
5
|
+
require 'chef/knife/openstack_helpers'
|
6
|
+
require 'chef/knife/cloud/openstack_service_options'
|
2
7
|
|
3
8
|
class Chef
|
4
9
|
class Knife
|
5
|
-
class
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
banner "knife openstack network list (options)"
|
10
|
+
class Cloud
|
11
|
+
class OpenstackNetworkList < ResourceListCommand
|
12
|
+
include OpenstackHelpers
|
13
|
+
include OpenstackServiceOptions
|
10
14
|
|
11
|
-
|
15
|
+
banner "knife openstack network list (options)"
|
12
16
|
|
13
|
-
|
17
|
+
def before_exec_command
|
18
|
+
#set columns_with_info map
|
19
|
+
@columns_with_info = [
|
20
|
+
{:label => 'Name', :key => 'name'},
|
21
|
+
{:label => 'ID', :key => 'id'},
|
22
|
+
{:label => 'Tenant', :key => 'tenant_id'},
|
23
|
+
{:label => 'Shared', :key => 'shared'}
|
24
|
+
]
|
25
|
+
@sort_by_field = "name"
|
26
|
+
end
|
14
27
|
|
15
|
-
|
16
|
-
|
17
|
-
ui.color('ID', :bold),
|
18
|
-
ui.color('Tenant', :bold),
|
19
|
-
ui.color('Shared', :bold),
|
20
|
-
]
|
21
|
-
network.networks.all.sort_by(&:name).each do |network|
|
22
|
-
net_list << network.name
|
23
|
-
net_list << network.id
|
24
|
-
net_list << network.tenant_id
|
25
|
-
net_list << network.shared.to_s
|
28
|
+
def query_resource
|
29
|
+
@service.list_networks
|
26
30
|
end
|
27
|
-
|
31
|
+
|
28
32
|
end
|
29
33
|
end
|
30
34
|
end
|
31
|
-
end
|
35
|
+
end
|
@@ -18,500 +18,214 @@
|
|
18
18
|
# limitations under the License.
|
19
19
|
#
|
20
20
|
|
21
|
-
require 'chef/knife/
|
22
|
-
require 'chef/knife/
|
21
|
+
require 'chef/knife/cloud/server/create_command'
|
22
|
+
require 'chef/knife/openstack_helpers'
|
23
|
+
require 'chef/knife/cloud/openstack_server_create_options'
|
24
|
+
require 'chef/knife/cloud/openstack_service'
|
25
|
+
require 'chef/knife/cloud/openstack_service_options'
|
26
|
+
require 'chef/knife/cloud/exceptions'
|
23
27
|
|
24
28
|
class Chef
|
25
29
|
class Knife
|
26
|
-
class
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
:short => "-N NAME",
|
70
|
-
:long => "--node-name NAME",
|
71
|
-
:description => "The Chef node name for your new node"
|
72
|
-
|
73
|
-
option :network_ids,
|
74
|
-
:long => "--network-ids NETWORK_ID_1,NETWORK_ID_2,NETWORK_ID_3",
|
75
|
-
:description => "Comma separated list of the UUID(s) of the network(s) for the server to attach",
|
76
|
-
:proc => Proc.new { |networks| networks.split(',') }
|
77
|
-
|
78
|
-
option :floating_ip,
|
79
|
-
:short => "-a [IP]",
|
80
|
-
:long => "--floating-ip [IP]",
|
81
|
-
:default => "-1",
|
82
|
-
:description => "Request to associate a floating IP address to the new OpenStack node. Assumes IPs have been allocated to the project. Specific IP is optional."
|
83
|
-
|
84
|
-
option :bootstrap_network,
|
85
|
-
:long => '--bootstrap-network NAME',
|
86
|
-
:default => 'public',
|
87
|
-
:description => "Specify network for bootstrapping. Default is 'public'."
|
88
|
-
|
89
|
-
option :network,
|
90
|
-
:long => "--no-network",
|
91
|
-
:boolean => true,
|
92
|
-
:default => true,
|
93
|
-
:description => "Use first available network for bootstrapping if 'public' and 'private' are unavailable."
|
94
|
-
|
95
|
-
option :private_network,
|
96
|
-
:long => "--private-network",
|
97
|
-
:description => "Use the private IP for bootstrapping rather than the public IP",
|
98
|
-
:boolean => true,
|
99
|
-
:default => false
|
100
|
-
|
101
|
-
option :ssh_key_name,
|
102
|
-
:short => "-S KEY",
|
103
|
-
:long => "--ssh-key KEY",
|
104
|
-
:description => "The OpenStack SSH keypair id",
|
105
|
-
:proc => Proc.new { |key| Chef::Config[:knife][:openstack_ssh_key_id] = key }
|
106
|
-
|
107
|
-
option :ssh_port,
|
108
|
-
:short => "-p PORT",
|
109
|
-
:long => "--ssh-port PORT",
|
110
|
-
:description => "The ssh port",
|
111
|
-
:default => "22",
|
112
|
-
:proc => Proc.new { |key| Chef::Config[:knife][:ssh_port] = key }
|
113
|
-
|
114
|
-
option :ssh_user,
|
115
|
-
:short => "-x USERNAME",
|
116
|
-
:long => "--ssh-user USERNAME",
|
117
|
-
:description => "The ssh username",
|
118
|
-
:default => "root"
|
119
|
-
|
120
|
-
option :ssh_password,
|
121
|
-
:short => "-P PASSWORD",
|
122
|
-
:long => "--ssh-password PASSWORD",
|
123
|
-
:description => "The ssh password"
|
124
|
-
|
125
|
-
option :identity_file,
|
126
|
-
:short => "-i IDENTITY_FILE",
|
127
|
-
:long => "--identity-file IDENTITY_FILE",
|
128
|
-
:description => "The SSH identity file used for authentication"
|
129
|
-
|
130
|
-
option :prerelease,
|
131
|
-
:long => "--prerelease",
|
132
|
-
:description => "Install the pre-release chef gems"
|
133
|
-
|
134
|
-
option :bootstrap_version,
|
135
|
-
:long => "--bootstrap-version VERSION",
|
136
|
-
:description => "The version of Chef to install",
|
137
|
-
:proc => Proc.new { |v| Chef::Config[:knife][:bootstrap_version] = v }
|
138
|
-
|
139
|
-
option :distro,
|
140
|
-
:short => "-d DISTRO",
|
141
|
-
:long => "--distro DISTRO",
|
142
|
-
:description => "Bootstrap a distro using a template; default is 'chef-full'",
|
143
|
-
:proc => Proc.new { |d| Chef::Config[:knife][:distro] = d },
|
144
|
-
:default => "chef-full"
|
145
|
-
|
146
|
-
option :template_file,
|
147
|
-
:long => "--template-file TEMPLATE",
|
148
|
-
:description => "Full path to location of template to use",
|
149
|
-
:proc => Proc.new { |t| Chef::Config[:knife][:template_file] = t },
|
150
|
-
:default => false
|
151
|
-
|
152
|
-
option :run_list,
|
153
|
-
:short => "-r RUN_LIST",
|
154
|
-
:long => "--run-list RUN_LIST",
|
155
|
-
:description => "Comma separated list of roles/recipes to apply",
|
156
|
-
:proc => lambda { |o| o.split(/[\s,]+/) },
|
157
|
-
:default => []
|
30
|
+
class Cloud
|
31
|
+
class OpenstackServerCreate < ServerCreateCommand
|
32
|
+
include OpenstackHelpers
|
33
|
+
include OpenstackServerCreateOptions
|
34
|
+
include OpenstackServiceOptions
|
35
|
+
|
36
|
+
|
37
|
+
banner "knife openstack server create (options)"
|
38
|
+
|
39
|
+
def before_exec_command
|
40
|
+
super
|
41
|
+
# setup the create options
|
42
|
+
@create_options = {
|
43
|
+
:server_def => {
|
44
|
+
#servers require a name, knife-cloud generates the chef_node_name
|
45
|
+
:name => config[:chef_node_name],
|
46
|
+
:image_ref => service.get_image(locate_config_value(:image)).id,
|
47
|
+
:flavor_ref => service.get_flavor(locate_config_value(:flavor)).id,
|
48
|
+
:security_groups => locate_config_value(:openstack_security_groups),
|
49
|
+
:availability_zone => locate_config_value(:availability_zone),
|
50
|
+
:metadata => locate_config_value(:metadata),
|
51
|
+
:key_name => locate_config_value(:openstack_ssh_key_id)
|
52
|
+
},
|
53
|
+
:server_create_timeout => locate_config_value(:server_create_timeout)
|
54
|
+
}
|
55
|
+
|
56
|
+
@create_options[:server_def].merge!({:user_data => locate_config_value(:user_data)}) if locate_config_value(:user_data)
|
57
|
+
@create_options[:server_def].merge!({:nics => locate_config_value(:network_ids).map { |nic| nic_id = { 'net_id' => nic }}}) if locate_config_value(:network_ids)
|
58
|
+
|
59
|
+
Chef::Log.debug("Create server params - server_def = #{@create_options[:server_def]}")
|
60
|
+
#set columns_with_info map
|
61
|
+
@columns_with_info = [
|
62
|
+
{:label => 'Instance ID', :key => 'id'},
|
63
|
+
{:label => 'Name', :key => 'name'},
|
64
|
+
{:label => 'Public IP', :key => 'addresses', :value_callback => method(:primary_public_ip_address)},
|
65
|
+
{:label => 'Private IP', :key => 'addresses', :value_callback => method(:primary_private_ip_address)},
|
66
|
+
{:label => 'Flavor', :key => 'flavor', :value_callback => method(:get_id)},
|
67
|
+
{:label => 'Image', :key => 'image', :value_callback => method(:get_id)},
|
68
|
+
{:label => 'Keypair', :key => 'key_name'},
|
69
|
+
{:label => 'State', :key => 'state'},
|
70
|
+
{:label => 'Availability Zone', :key => 'availability_zone'}
|
71
|
+
]
|
72
|
+
end
|
158
73
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
:boolean => true,
|
163
|
-
:default => true
|
74
|
+
def get_id(value)
|
75
|
+
value['id']
|
76
|
+
end
|
164
77
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
78
|
+
# Setup the floating ip after server creation.
|
79
|
+
def after_exec_command
|
80
|
+
Chef::Log.debug("Addresses #{server.addresses}")
|
81
|
+
msg_pair("Public IP Address", primary_public_ip_address(server.addresses)) if primary_public_ip_address(server.addresses)
|
82
|
+
msg_pair("Private IP Address", primary_private_ip_address(server.addresses)) if primary_private_ip_address(server.addresses)
|
83
|
+
|
84
|
+
floating_address = locate_config_value(:openstack_floating_ip)
|
85
|
+
bind_ip = primary_network_ip_address(server.addresses,server.addresses.keys[0])
|
86
|
+
Chef::Log.debug("Floating IP Address requested #{floating_address}")
|
87
|
+
unless (floating_address == '-1') #no floating IP requested
|
88
|
+
addresses = service.connection.addresses
|
89
|
+
#floating requested without value
|
90
|
+
if floating_address.nil?
|
91
|
+
free_floating = addresses.find_index {|a| a.fixed_ip.nil?}
|
92
|
+
begin
|
93
|
+
if free_floating.nil? #no free floating IP found
|
94
|
+
error_message = "Unable to assign a Floating IP from allocated IPs."
|
95
|
+
ui.fatal(error_message)
|
96
|
+
raise CloudExceptions::ServerSetupError, error_message
|
97
|
+
else
|
98
|
+
floating_address = addresses[free_floating].ip
|
99
|
+
end
|
100
|
+
rescue CloudExceptions::ServerSetupError => e
|
101
|
+
cleanup_on_failure
|
102
|
+
raise e
|
103
|
+
end
|
104
|
+
end
|
169
105
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
:proc => Proc.new { |v| Chef::Config[:knife][:bootstrap_proxy] = v }
|
106
|
+
# Pull the port_id for the associate_floating_ip
|
107
|
+
port_id = @service.network.list_ports[:body].flatten.flatten[1]["id"]
|
108
|
+
fixed_ip_address = @service.network.list_ports[:body].flatten.flatten[1]["fixed_ips"].flatten[0]["ip_address"]
|
174
109
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
:default => 600,
|
179
|
-
:proc => Proc.new { |v| Chef::Config[:knife][:server_create_timeouts] = v }
|
110
|
+
floating_ip_id = get_floating_ip_id(floating_address)
|
111
|
+
# Associate the floating ip via the neutron/network api
|
112
|
+
@service.network.associate_floating_ip(floating_ip_id, port_id, options = {:fixed_ip_address => fixed_ip_address })
|
180
113
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
:proc => lambda { |o| JSON.parse(o) },
|
186
|
-
:default => {}
|
114
|
+
#a bit of a hack, but server.reload takes a long time
|
115
|
+
(server.addresses['public'] ||= []) << {"version"=>4,"addr"=>floating_address}
|
116
|
+
msg_pair("Floating IP Address", floating_address)
|
117
|
+
end
|
187
118
|
|
188
|
-
|
189
|
-
|
190
|
-
:description => "The file path containing user data information for this server",
|
191
|
-
:proc => Proc.new { |user_data| open(user_data) { |f| f.read } }
|
119
|
+
Chef::Log.debug("Addresses #{server.addresses}")
|
120
|
+
Chef::Log.debug("Public IP Address actual: #{primary_public_ip_address(server.addresses)}") if primary_public_ip_address(server.addresses)
|
192
121
|
|
193
|
-
|
194
|
-
|
195
|
-
readable = IO.select([tcp_socket], nil, nil, 5)
|
196
|
-
if readable
|
197
|
-
Chef::Log.debug("sshd accepting connections on #{hostname} port #{port}, banner is #{tcp_socket.gets}")
|
198
|
-
yield
|
199
|
-
true
|
200
|
-
else
|
201
|
-
false
|
122
|
+
msg_pair("Private IP Address", primary_private_ip_address(server.addresses)) if primary_private_ip_address(server.addresses)
|
123
|
+
super
|
202
124
|
end
|
203
|
-
rescue Errno::ETIMEDOUT
|
204
|
-
false
|
205
|
-
rescue Errno::EPERM
|
206
|
-
false
|
207
|
-
rescue Errno::ECONNREFUSED
|
208
|
-
sleep 2
|
209
|
-
false
|
210
|
-
rescue Errno::EHOSTUNREACH, Errno::ENETUNREACH
|
211
|
-
sleep 2
|
212
|
-
false
|
213
|
-
rescue Errno::ENETUNREACH
|
214
|
-
sleep 2
|
215
|
-
false
|
216
|
-
ensure
|
217
|
-
tcp_socket && tcp_socket.close
|
218
|
-
end
|
219
125
|
|
220
|
-
|
221
|
-
|
222
|
-
return true
|
223
|
-
rescue SocketError
|
224
|
-
sleep 2
|
225
|
-
false
|
226
|
-
rescue Errno::ETIMEDOUT
|
227
|
-
false
|
228
|
-
rescue Errno::EPERM
|
229
|
-
false
|
230
|
-
rescue Errno::ECONNREFUSED
|
231
|
-
sleep 2
|
232
|
-
false
|
233
|
-
rescue Errno::EHOSTUNREACH
|
234
|
-
sleep 2
|
235
|
-
false
|
236
|
-
rescue Errno::ENETUNREACH
|
237
|
-
sleep 2
|
238
|
-
false
|
239
|
-
end
|
126
|
+
def before_bootstrap
|
127
|
+
super
|
240
128
|
|
241
|
-
|
242
|
-
|
243
|
-
require 'em-winrm'
|
244
|
-
require 'chef/knife/bootstrap_windows_winrm'
|
245
|
-
require 'chef/knife/core/windows_bootstrap_context'
|
246
|
-
require 'chef/knife/winrm'
|
247
|
-
end
|
248
|
-
def run
|
249
|
-
$stdout.sync = true
|
129
|
+
# Use SSH password either specified from command line or from openstack server instance
|
130
|
+
config[:ssh_password] = locate_config_value(:ssh_password) || server.password unless config[:openstack_ssh_key_id]
|
250
131
|
|
251
|
-
|
252
|
-
|
253
|
-
load_winrm_deps
|
254
|
-
else
|
255
|
-
# workaround for KNIFE-296 winrm values stomping on ssh values
|
256
|
-
# unchanged ssh_user and changed winrm_user, override ssh_user
|
257
|
-
if locate_config_value(:ssh_user).eql?(options[:ssh_user][:default]) &&
|
258
|
-
!locate_config_value(:winrm_user).eql?(options[:winrm_user][:default])
|
259
|
-
config[:ssh_user] = locate_config_value(:winrm_user)
|
260
|
-
end
|
261
|
-
# unchanged ssh_port and changed winrm_port, override ssh_port
|
262
|
-
if locate_config_value(:ssh_port).eql?(options[:ssh_port][:default]) &&
|
263
|
-
!locate_config_value(:winrm_port).eql?(options[:winrm_port][:default])
|
264
|
-
config[:ssh_port] = locate_config_value(:winrm_port)
|
265
|
-
end
|
266
|
-
# unset ssh_password and set winrm_password, override ssh_password
|
267
|
-
if locate_config_value(:ssh_password).nil? &&
|
268
|
-
!locate_config_value(:winrm_password).nil?
|
269
|
-
config[:ssh_password] = locate_config_value(:winrm_password)
|
270
|
-
end
|
271
|
-
# unset identity_file and set kerberos_keytab_file, override identity_file
|
272
|
-
if locate_config_value(:identity_file).nil? &&
|
273
|
-
!locate_config_value(:kerberos_keytab_file).nil?
|
274
|
-
config[:identity_file] = locate_config_value(:kerberos_keytab_file)
|
275
|
-
end
|
276
|
-
end
|
277
|
-
# servers require a name, generate one if not passed
|
278
|
-
node_name = get_node_name(config[:chef_node_name])
|
132
|
+
# private_network means bootstrap_network = 'private'
|
133
|
+
config[:bootstrap_network] = 'private' if config[:private_network]
|
279
134
|
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
}
|
290
|
-
server_def[:user_data] = locate_config_value(:user_data) unless locate_config_value(:user_data).nil?
|
291
|
-
unless locate_config_value(:network_ids).nil?
|
292
|
-
server_def[:nics] = locate_config_value(:network_ids).map do |nic|
|
293
|
-
nic_id = { 'net_id' => nic }
|
294
|
-
nic_id
|
135
|
+
# Which IP address to bootstrap
|
136
|
+
unless config[:network] # --no-network
|
137
|
+
bootstrap_ip_address = primary_public_ip_address(server.addresses) ||
|
138
|
+
primary_private_ip_address(server.addresses) ||
|
139
|
+
server.addresses.first[1][0]['addr']
|
140
|
+
Chef::Log.debug("No Bootstrap Network: #{config[:bootstrap_network]}")
|
141
|
+
else
|
142
|
+
bootstrap_ip_address = primary_network_ip_address(server.addresses, config[:bootstrap_network])
|
143
|
+
Chef::Log.debug("Bootstrap Network: #{config[:bootstrap_network]}")
|
295
144
|
end
|
296
|
-
end
|
297
|
-
Chef::Log.debug("server_def is: #{server_def}")
|
298
145
|
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
Chef::Log.debug("Security Groups #{locate_config_value(:security_groups)}")
|
305
|
-
Chef::Log.debug("User Data #{locate_config_value(:user_data)}")
|
306
|
-
Chef::Log.debug("Metadata #{locate_config_value(:metadata)}")
|
307
|
-
Chef::Log.debug("Creating server #{server_def}")
|
308
|
-
|
309
|
-
begin
|
310
|
-
server = connection.servers.create(server_def)
|
311
|
-
rescue Excon::Errors::BadRequest => e
|
312
|
-
response = Chef::JSONCompat.from_json(e.response.body)
|
313
|
-
if response['badRequest']['code'] == 400
|
314
|
-
if response['badRequest']['message'] =~ /Invalid flavorRef/
|
315
|
-
ui.fatal("Bad request (400): Invalid flavor specified: #{server_def[:flavor_ref]}")
|
316
|
-
exit 1
|
317
|
-
else
|
318
|
-
ui.fatal("Bad request (400): #{response['badRequest']['message']}")
|
319
|
-
exit 1
|
320
|
-
end
|
321
|
-
else
|
322
|
-
ui.fatal("Unknown server error (#{response['badRequest']['code']}): #{response['badRequest']['message']}")
|
323
|
-
raise e
|
146
|
+
Chef::Log.debug("Bootstrap IP Address: #{bootstrap_ip_address}")
|
147
|
+
if bootstrap_ip_address.nil?
|
148
|
+
error_message = "No IP address available for bootstrapping."
|
149
|
+
ui.error(error_message)
|
150
|
+
raise CloudExceptions::BootstrapError, error_message
|
324
151
|
end
|
152
|
+
config[:bootstrap_ip_address] = bootstrap_ip_address
|
325
153
|
end
|
326
154
|
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
print "\n#{ui.color("Waiting for server", :magenta)}"
|
332
|
-
|
333
|
-
# wait for it to be ready to do stuff
|
334
|
-
server.wait_for(Integer(locate_config_value(:server_create_timeout))) { print "."; ready? }
|
335
|
-
|
336
|
-
puts("\n")
|
155
|
+
def validate_params!
|
156
|
+
# set param vm_name to a random value if the name is not set by the user (plugin)
|
157
|
+
config[:chef_node_name] = get_node_name(locate_config_value(:chef_node_name), locate_config_value(:chef_node_name_prefix))
|
337
158
|
|
338
|
-
|
339
|
-
msg_pair("Image", server.image['id'])
|
340
|
-
msg_pair("SSH Identity File", config[:identity_file])
|
341
|
-
msg_pair("SSH Keypair", server.key_name) if server.key_name
|
342
|
-
msg_pair("SSH Password", server.password) if (server.password && !server.key_name)
|
159
|
+
errors = []
|
343
160
|
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
floating_address = locate_config_value(:floating_ip)
|
349
|
-
Chef::Log.debug("Floating IP Address requested #{floating_address}")
|
350
|
-
unless (floating_address == '-1') # no floating IP requested
|
351
|
-
addresses = connection.addresses
|
352
|
-
# floating requested without value
|
353
|
-
if floating_address.nil?
|
354
|
-
free_floating = addresses.find_index { |a| a.fixed_ip.nil? }
|
355
|
-
if free_floating.nil? # no free floating IP found
|
356
|
-
ui.error("Unable to assign a Floating IP from allocated IPs.")
|
357
|
-
exit 1
|
358
|
-
else
|
359
|
-
floating_address = addresses[free_floating].ip
|
161
|
+
if locate_config_value(:bootstrap_protocol) == 'winrm'
|
162
|
+
if locate_config_value(:winrm_password).nil?
|
163
|
+
errors << "You must provide Winrm Password."
|
360
164
|
end
|
165
|
+
elsif locate_config_value(:bootstrap_protocol) != 'ssh'
|
166
|
+
errors << "You must provide a valid bootstrap protocol. options [ssh/winrm]. For linux type images, options [ssh]"
|
361
167
|
end
|
362
|
-
server.associate_address(floating_address)
|
363
|
-
# bit of a hack, but server.reload takes a long time
|
364
|
-
(server.addresses['public'] ||= []) << { "version" => 4, "addr" => floating_address }
|
365
|
-
msg_pair("Floating IP Address", floating_address)
|
366
|
-
end
|
367
|
-
|
368
|
-
Chef::Log.debug("Addresses #{server.addresses}")
|
369
|
-
Chef::Log.debug("Public IP Address actual: #{primary_public_ip_address(server.addresses)}") if primary_public_ip_address(server.addresses)
|
370
168
|
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
unless config[:network] # --no-network
|
375
|
-
bootstrap_ip_address = primary_public_ip_address(server.addresses) ||
|
376
|
-
primary_private_ip_address(server.addresses) ||
|
377
|
-
server.addresses[1][0]['addr']
|
378
|
-
Chef::Log.debug("No Bootstrap Network: #{config[:bootstrap_network]}")
|
379
|
-
else
|
380
|
-
bootstrap_ip_address = primary_network_ip_address(server.addresses, config[:bootstrap_network])
|
381
|
-
Chef::Log.debug("Bootstrap Network: #{config[:bootstrap_network]}")
|
169
|
+
errors << "You must provide --image-os-type option [windows/linux]" if ! (%w(windows linux).include?(locate_config_value(:image_os_type)))
|
170
|
+
error_message = ""
|
171
|
+
raise CloudExceptions::ValidationError, error_message if errors.each{|e| ui.error(e); error_message = "#{error_message} #{e}."}.any?
|
382
172
|
end
|
383
173
|
|
384
|
-
|
385
|
-
|
386
|
-
ui.error("No IP address available for bootstrapping.")
|
387
|
-
exit 1
|
174
|
+
def is_image_valid?
|
175
|
+
service.get_image(locate_config_value(:image)).nil? ? false : true
|
388
176
|
end
|
389
177
|
|
390
|
-
|
391
|
-
|
392
|
-
print(".") until tcp_test_winrm(bootstrap_ip_address, locate_config_value(:winrm_port))
|
393
|
-
bootstrap_for_windows_node(server, bootstrap_ip_address).run
|
394
|
-
else
|
395
|
-
Chef::Log.debug("Waiting for sshd on IP address: #{bootstrap_ip_address} and port: #{locate_config_value(:ssh_port)}")
|
396
|
-
print "\n#{ui.color("Waiting for sshd", :magenta)}"
|
397
|
-
print(".") until tcp_test_ssh(bootstrap_ip_address, locate_config_value(:ssh_port)) {
|
398
|
-
sleep @initial_sleep_delay ||= 10
|
399
|
-
puts("done")
|
400
|
-
}
|
401
|
-
bootstrap_for_node(server, bootstrap_ip_address).run
|
402
|
-
end
|
403
|
-
puts "\n"
|
404
|
-
msg_pair("Instance Name", server.name)
|
405
|
-
msg_pair("Instance ID", server.id)
|
406
|
-
msg_pair("Flavor", server.flavor['id'])
|
407
|
-
msg_pair("Image", server.image['id'])
|
408
|
-
msg_pair("SSH Keypair", server.key_name) if server.key_name
|
409
|
-
msg_pair("SSH Password", server.password) if (server.password && !server.key_name)
|
410
|
-
server.addresses.each do |name,addr|
|
411
|
-
msg_pair("Network", name)
|
412
|
-
msg_pair(" IP Address", addr[0]['addr'])
|
178
|
+
def is_flavor_valid?
|
179
|
+
service.get_flavor(locate_config_value(:flavor)).nil? ? false : true
|
413
180
|
end
|
414
|
-
msg_pair("Environment", config[:environment] || '_default')
|
415
|
-
msg_pair("Run List", config[:run_list].join(', '))
|
416
|
-
end
|
417
|
-
|
418
|
-
def bootstrap_for_windows_node(server, bootstrap_ip_address)
|
419
|
-
bootstrap = Chef::Knife::BootstrapWindowsWinrm.new
|
420
|
-
bootstrap.name_args = [bootstrap_ip_address]
|
421
|
-
bootstrap.config[:winrm_user] = locate_config_value(:winrm_user) || 'Administrator'
|
422
|
-
bootstrap.config[:winrm_password] = locate_config_value(:winrm_password)
|
423
|
-
bootstrap.config[:winrm_transport] = locate_config_value(:winrm_transport)
|
424
|
-
bootstrap.config[:winrm_port] = locate_config_value(:winrm_port)
|
425
|
-
bootstrap_common_params(bootstrap, server.name)
|
426
|
-
end
|
427
181
|
|
428
|
-
|
429
|
-
|
430
|
-
bootstrap.config[:run_list] = config[:run_list]
|
431
|
-
bootstrap.config[:first_boot_attributes] = config[:first_boot_attributes]
|
432
|
-
bootstrap.config[:prerelease] = config[:prerelease]
|
433
|
-
bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
|
434
|
-
bootstrap.config[:distro] = locate_config_value(:distro)
|
435
|
-
bootstrap.config[:template_file] = locate_config_value(:template_file)
|
436
|
-
bootstrap.config[:bootstrap_proxy] = locate_config_value(:bootstrap_proxy)
|
437
|
-
bootstrap.config[:environment] = config[:environment]
|
438
|
-
bootstrap.config[:encrypted_data_bag_secret] = config[:encrypted_data_bag_secret]
|
439
|
-
bootstrap.config[:encrypted_data_bag_secret_file] = config[:encrypted_data_bag_secret_file]
|
440
|
-
# let ohai know we're using OpenStack
|
441
|
-
Chef::Config[:knife][:hints] ||= {}
|
442
|
-
Chef::Config[:knife][:hints]['openstack'] ||= {}
|
443
|
-
bootstrap
|
444
|
-
end
|
445
|
-
|
446
|
-
def bootstrap_for_node(server, bootstrap_ip_address)
|
447
|
-
bootstrap = Chef::Knife::Bootstrap.new
|
448
|
-
bootstrap.name_args = [bootstrap_ip_address]
|
449
|
-
bootstrap.config[:ssh_user] = config[:ssh_user]
|
450
|
-
bootstrap.config[:ssh_password] = config[:ssh_password] || server.password unless config[:ssh_key_name]
|
451
|
-
bootstrap.config[:ssh_port] = config[:ssh_port]
|
452
|
-
bootstrap.config[:identity_file] = config[:identity_file]
|
453
|
-
bootstrap.config[:host_key_verify] = config[:host_key_verify]
|
454
|
-
bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root'
|
455
|
-
bootstrap_common_params(bootstrap, server.name)
|
456
|
-
end
|
457
|
-
|
458
|
-
def flavor
|
459
|
-
@flavor ||= connection.flavors.find{|f| f.name == locate_config_value(:flavor) || f.id == locate_config_value(:flavor) }
|
460
|
-
end
|
182
|
+
def is_floating_ip_valid?
|
183
|
+
address = locate_config_value(:openstack_floating_ip)
|
461
184
|
|
462
|
-
|
463
|
-
@image ||= connection.images.find{|img| img.name =~ /#{locate_config_value(:image)}/ || img.id == locate_config_value(:image) }
|
464
|
-
end
|
465
|
-
|
466
|
-
def is_floating_ip_valid
|
467
|
-
address = locate_config_value(:floating_ip)
|
468
|
-
if address == '-1' # no floating IP requested
|
469
|
-
return true
|
470
|
-
end
|
471
|
-
addresses = connection.addresses
|
472
|
-
return false if addresses.empty? # no floating IPs
|
473
|
-
# floating requested without value
|
474
|
-
if address.nil?
|
475
|
-
if addresses.find_index { |a| a.fixed_ip.nil? }
|
185
|
+
if address == '-1' # no floating IP requested
|
476
186
|
return true
|
477
|
-
else
|
478
|
-
return false # no floating IPs available
|
479
187
|
end
|
480
|
-
|
481
|
-
|
482
|
-
if addresses.
|
483
|
-
|
188
|
+
|
189
|
+
addresses = service.connection.addresses
|
190
|
+
return false if addresses.empty? # no floating IPs
|
191
|
+
# floating requested without value
|
192
|
+
if address.nil?
|
193
|
+
if addresses.find_index { |a| a.fixed_ip.nil? }
|
194
|
+
return true
|
195
|
+
else
|
196
|
+
return false # no floating IPs available
|
197
|
+
end
|
484
198
|
else
|
485
|
-
|
199
|
+
# floating requested with value
|
200
|
+
if addresses.find_index { |a| a.ip == address }
|
201
|
+
return true
|
202
|
+
else
|
203
|
+
return false # requested floating IP does not exist
|
204
|
+
end
|
486
205
|
end
|
487
206
|
end
|
488
|
-
end
|
489
|
-
|
490
|
-
def validate!
|
491
|
-
super([:image, :openstack_username, :openstack_password, :openstack_auth_url])
|
492
|
-
|
493
|
-
if flavor.nil?
|
494
|
-
ui.error("You have not provided a valid flavor ID. Please note the options for this value are -f or --flavor.")
|
495
|
-
exit 1
|
496
|
-
end
|
497
207
|
|
498
|
-
|
499
|
-
|
500
|
-
|
208
|
+
def post_connection_validations
|
209
|
+
errors = []
|
210
|
+
errors << "You have not provided a valid image ID. Please note the options for this value are -I or --image." if !is_image_valid?
|
211
|
+
errors << "You have not provided a valid flavor ID. Please note the options for this value are -f or --flavor." if !is_flavor_valid?
|
212
|
+
errors << "You have either requested an invalid floating IP address or none are available." if !is_floating_ip_valid?
|
213
|
+
error_message = ""
|
214
|
+
raise CloudExceptions::ValidationError, error_message if errors.each{|e| ui.error(e); error_message = "#{error_message} #{e}."}.any?
|
501
215
|
end
|
502
216
|
|
503
|
-
|
504
|
-
|
505
|
-
|
217
|
+
def get_floating_ip_id(floating_address)
|
218
|
+
# required for this method to work
|
219
|
+
floating_ip_id = -1
|
220
|
+
# Figure out the id for the port that the floating ip you requested
|
221
|
+
@service.network.list_floating_ips[:body]["floatingips"].each do |x|
|
222
|
+
if x["floating_ip_address"] == floating_address
|
223
|
+
floating_ip_id = x["id"]
|
224
|
+
end
|
225
|
+
end
|
226
|
+
return floating_ip_id
|
506
227
|
end
|
507
228
|
end
|
508
|
-
|
509
|
-
# generate a random name if chef_node_name is empty
|
510
|
-
def get_node_name(chef_node_name)
|
511
|
-
return chef_node_name unless chef_node_name.nil?
|
512
|
-
# lazy uuids
|
513
|
-
chef_node_name = "os-" + rand.to_s.split('.')[1]
|
514
|
-
end
|
515
229
|
end
|
516
230
|
end
|
517
231
|
end
|