knife-openstack 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
1
+ ## v0.7.0
2
+ * Update dependency on to Fog 1.10.0
3
+ * 'delay-loading' changes to reduce load-time (Mohit Sethi)
4
+ * Use the hint with the bootstrap method instead of assuming the :personality works with the server.create method (KNIFE-201)
5
+ * Added 'knife openstack group list' for listing security groups and their rules (KNIFE-227)
6
+ * Filter out extraneous images from knife openstack image list and added '--disable-filter' to disable
7
+ * Fixed minor issue for public ip addresses (Edmund Haselwanter)
8
+ * Fixed security groups, adding `-G` support (KNIFE-230)
9
+ * Added snapshots as a new column in image list
10
+ * "knife openstack image list" fails with empty image name (KNIFE-83)(Simon Belluzzo)
11
+ * excon / fog errors are a JSON blob, Rescue fog errors (KNIFE-87)(Bryan McLellan)
12
+ * Better error handling for connection errors.
13
+ * Pass ssh_password to bootstrap (KNIFE-88)(David Petzel)
14
+ * Catch Net Unreachable error (E.J. Finneran)
15
+ * Allow an option to ignore the SSL cert (KNIFE-225)(BK Box)
16
+ * Attach to floating IPs (Mohit Sethi)
17
+ * Key pair is not required (KNIFE-226)(BK Box)
18
+ * Fog 1.10.0 changes API for OpenStack IP addresses (KNIFE-248)
19
+
1
20
  ## v0.6.2
2
21
  * Use less pessimistic fog version constraint.
3
22
  * Add guards to protect against nil values for private_ip_address
@@ -20,20 +39,22 @@
20
39
  * Added support for `--no-host-key-verify` (Lamont Granquist)
21
40
  * Added support for `--private-network` for bootstrapping private network
22
41
 
23
- ## v0.5.2
42
+ ## V0.5.2
24
43
  * initial Cactus release using EC2 API
25
44
 
26
45
  # BACKLOG/ISSUES #
27
- This is a list of features currently lacking and (eventually) under development:
46
+ This is a list of missing(?) features and open questions currently under development consideration:
28
47
 
29
- * security groups are still broken, appear to be broken in Fog. Add `-G` support for security groups other than 'default'
48
+ * Basic availability zones support (Jarek Zmudzinski) NEED TESTING ACCESS FOR AVAILABILITY ZONES
49
+ * Windows bootstrapping (winrm-based) support for knife-openstack (KNIFE-221) winrm branch, UGLY WARNINGS NEED RESOLUTION
30
50
  * purge only works when names match up with clients
31
- * `knife openstack floating list|associate|disassociate ip|node`
32
- * take either the flavor ID or the flavor name
33
- * take either the image ID or the image name
34
- * more information in `knife openstack image list`
35
- * get DNS names working
36
- * availability zones
37
- * filter out the *-initrd and *-kernel from 'openstack image list'. Fog -> container_format={aki|ari} or disk_format on the same params
51
+ * `knife openstack floating list|associate|release NODE` with --floating-ip-pool also
52
+ * Allow specifying the name of the pool when using floating IPs (KNIFE-229)
53
+ * attempt to allocate a floating ipaddress if none if free, currently missing in Fog
54
+ * take either the flavor ID or the flavor name (KNIFE-76)
55
+ * take either the image ID or the image name (similar for KNIFE-76)
56
+ * server create with expired password hangs (KNIFE-86)
57
+ * added ability to specify arbitrary network ID (KNIFE-231)
38
58
  * assumption of only single floating IP (and fog uses the last as the public_ip_address)
39
59
  * probably other places public network is assumed that could cause issues
60
+ * fog is putting the original public IP address into the private_ip_address method when you get a floating_ip, this is wrong. Remove KNIFE-248 code once fixed.
data/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  Knife OpenStack
2
2
  ===============
3
3
 
4
- This is the official Opscode Knife plugin for OpenStack Compute (Nova). This plugin gives knife the ability to create, bootstrap and manage instances in OpenStack Compute clouds. It has been tested against the `Diablo` and `Essex` releases in configurations using Keystone against the OpenStack API (as opposed to the EC2 API).
4
+ This is the official Opscode Knife plugin for OpenStack Compute (Nova). This plugin gives knife the ability to create, bootstrap and manage instances in OpenStack Compute clouds. It has been tested against the `Diablo` through pre-`Grizzly` releases in configurations using Keystone against the OpenStack API (as opposed to the EC2 API).
5
5
 
6
- Please refer to the CHANGELOG.md for version history and known limitations. If you are using Floating IP addresses, please refer to the "Working with Floating IPs" section below.
6
+ Please refer to the [CHANGELOG](CHANGELOG.md) for version history and known issues.
7
7
 
8
8
  # Installation #
9
9
 
@@ -34,6 +34,10 @@ If your knife.rb file will be checked into a SCM system (ie readable by others)
34
34
  knife[:openstack_auth_url] = "#{ENV['OS_AUTH_URL']}"
35
35
  knife[:openstack_tenant] = "#{ENV['OS_TENANT_NAME']}"
36
36
 
37
+ If your OpenStack deployment is over SSL, but does not have a valid certificate, you can add the following option to bypass SSL check:
38
+
39
+ knife[:openstack_insecure] = true
40
+
37
41
  You also have the option of passing your OpenStack API Username/Password into the individual knife subcommands using the `-A` (or `--openstack-username`) `-K` (or `--openstack-password`) command options
38
42
 
39
43
  # provision a new image named kb01
@@ -72,12 +76,17 @@ Outputs a list of all servers in the currently configured OpenStack Compute clou
72
76
  knife openstack flavor list
73
77
  ---------------------------
74
78
 
75
- Outputs a list of all available flavors (available hardware configuration for a server) available to the currently configured OpenStack Compute cloud account. Each flavor has a unique combination of virtual cpus, disk space and memory capacity. This data can be useful when choosing a flavor id to pass to the `knife openstack server create` subcommand.
79
+ Outputs a list of all available flavors (available hardware configuration for a server) available to the currently configured OpenStack Compute cloud account. Each flavor has a unique combination of virtual cpus, disk space and memory capacity. This data may be useful when choosing a flavor id to pass to the `knife openstack server create` subcommand.
76
80
 
77
81
  knife openstack image list
78
82
  --------------------------
79
83
 
80
- Outputs a list of all available images available to the currently configured OpenStack Compute cloud account. An image is a collection of files used to create or rebuild a server. This data can be useful when choosing an image id to pass to the `knife openstack server create` subcommand.
84
+ Outputs a list of all available images and snapshots available to the currently configured OpenStack Compute cloud account. An image is a collection of files used to create or rebuild a server. The retuned list filters out image names ending in 'initrd', 'kernel', 'loader', 'virtual' or 'vmlinuz' (this may be disabled with `--disable-filter`). This data may be useful when choosing an image id to pass to the `knife openstack server create` subcommand.
85
+
86
+ knife openstack group list
87
+ --------------------
88
+
89
+ Outputs a list of the security groups available to the currently configured OpenStack Compute cloud account. Each group may have multiple rules. This data may be useful when choosing your security group(s) to pass to the `knife openstack server create` subcommand.
81
90
 
82
91
  # License #
83
92
 
@@ -85,7 +94,7 @@ Author:: Seth Chisamore (<schisamo@opscode.com>)
85
94
 
86
95
  Author:: Matt Ray (<matt@opscode.com>)
87
96
 
88
- Copyright:: Copyright (c) 2011-2012 Opscode, Inc.
97
+ Copyright:: Copyright (c) 2011-2013 Opscode, Inc.
89
98
 
90
99
  License:: Apache License, Version 2.0
91
100
 
@@ -19,6 +19,6 @@ Gem::Specification.new do |s|
19
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
20
  s.require_paths = ["lib"]
21
21
 
22
- s.add_dependency "fog", "~> 1.4"
22
+ s.add_dependency "fog", ">= 1.10.0"
23
23
  s.add_dependency "chef", ">= 0.10.10"
24
24
  end
@@ -1,7 +1,7 @@
1
1
  #
2
2
  # Author:: Seth Chisamore (<schisamo@opscode.com>)
3
3
  # Author:: Matt Ray (<matt@opscode.com>)
4
- # Copyright:: Copyright (c) 2011-2012 Opscode, Inc.
4
+ # Copyright:: Copyright (c) 2011-2013 Opscode, Inc.
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,7 @@
17
17
  # limitations under the License.
18
18
  #
19
19
 
20
- require 'chef/knife'
20
+ require 'fog'
21
21
 
22
22
  class Chef
23
23
  class Knife
@@ -30,9 +30,10 @@ class Chef
30
30
  includer.class_eval do
31
31
 
32
32
  deps do
33
- require 'fog'
34
- require 'readline'
35
33
  require 'chef/json_compat'
34
+ require 'chef/knife'
35
+ require 'readline'
36
+ Chef::Knife.load_deps
36
37
  end
37
38
 
38
39
  option :openstack_username,
@@ -57,6 +58,13 @@ class Chef
57
58
  :long => "--openstack-api-endpoint ENDPOINT",
58
59
  :description => "Your OpenStack API endpoint",
59
60
  :proc => Proc.new { |endpoint| Chef::Config[:knife][:openstack_auth_url] = endpoint }
61
+
62
+ option :openstack_insecure,
63
+ :long => "--insecure",
64
+ :description => "Ignore SSL certificate on the Auth URL",
65
+ :boolean => true,
66
+ :default => false,
67
+ :proc => Proc.new { |key| Chef::Config[:knife][:openstack_insecure] = key }
60
68
  end
61
69
  end
62
70
 
@@ -64,15 +72,26 @@ class Chef
64
72
  Chef::Log.debug("openstack_username #{Chef::Config[:knife][:openstack_username]}")
65
73
  Chef::Log.debug("openstack_auth_url #{Chef::Config[:knife][:openstack_auth_url]}")
66
74
  Chef::Log.debug("openstack_tenant #{Chef::Config[:knife][:openstack_tenant]}")
75
+ Chef::Log.debug("openstack_insecure #{Chef::Config[:knife][:openstack_insecure].to_s}")
76
+
67
77
  @connection ||= begin
68
78
  connection = Fog::Compute.new(
69
79
  :provider => 'OpenStack',
70
80
  :openstack_username => Chef::Config[:knife][:openstack_username],
71
81
  :openstack_api_key => Chef::Config[:knife][:openstack_password],
72
82
  :openstack_auth_url => Chef::Config[:knife][:openstack_auth_url],
73
- :openstack_tenant => Chef::Config[:knife][:openstack_tenant]
74
- )
75
- end
83
+ :openstack_tenant => Chef::Config[:knife][:openstack_tenant],
84
+ :connection_options => {
85
+ :ssl_verify_peer => !Chef::Config[:knife][:openstack_insecure]
86
+ }
87
+ )
88
+ rescue Excon::Errors::Unauthorized => e
89
+ ui.fatal("Connection failure, please check your OpenStack username and password.")
90
+ exit 1
91
+ rescue Excon::Errors::SocketError => e
92
+ ui.fatal("Connection failure, please check your OpenStack authentication URL.")
93
+ exit 1
94
+ end
76
95
  end
77
96
 
78
97
  def locate_config_value(key)
@@ -101,6 +120,20 @@ class Chef
101
120
  end
102
121
  end
103
122
 
123
+ # http://tickets.opscode.com/browse/KNIFE-248
124
+ def primary_private_ip_address(addresses)
125
+ if addresses['private']
126
+ return addresses['private'].last['addr']
127
+ end
128
+ end
129
+
130
+ #we use last since the floating IP goes there
131
+ def primary_public_ip_address(addresses)
132
+ if addresses['public']
133
+ return addresses['public'].last['addr']
134
+ end
135
+ end
136
+
104
137
  end
105
138
  end
106
139
  end
@@ -38,12 +38,18 @@ class Chef
38
38
  ui.color('RAM', :bold),
39
39
  ui.color('Disk', :bold),
40
40
  ]
41
- connection.flavors.sort_by(&:id).each do |flavor|
42
- flavor_list << flavor.id.to_s
43
- flavor_list << flavor.name
44
- flavor_list << flavor.vcpus.to_s
45
- flavor_list << "#{flavor.ram.to_s} MB"
46
- flavor_list << "#{flavor.disk.to_s} GB"
41
+ begin
42
+ connection.flavors.sort_by(&:id).each do |flavor|
43
+ flavor_list << flavor.id.to_s
44
+ flavor_list << flavor.name
45
+ flavor_list << flavor.vcpus.to_s
46
+ flavor_list << "#{flavor.ram.to_s} MB"
47
+ flavor_list << "#{flavor.disk.to_s} GB"
48
+ end
49
+ rescue Excon::Errors::BadRequest => e
50
+ response = Chef::JSONCompat.from_json(e.response.body)
51
+ ui.fatal("Unknown server error (#{response['badRequest']['code']}): #{response['badRequest']['message']}")
52
+ raise e
47
53
  end
48
54
  puts ui.list(flavor_list, :uneven_columns_across, 5)
49
55
  end
@@ -0,0 +1,57 @@
1
+ #
2
+ # Author:: Matt Ray (<matt@opscode.com>)
3
+ # Copyright:: Copyright (c) 2013 Opscode, Inc.
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'
20
+
21
+ class Chef
22
+ class Knife
23
+ class OpenstackGroupList < Knife
24
+
25
+ include Knife::OpenstackBase
26
+
27
+ banner "knife openstack group list (options)"
28
+
29
+ def run
30
+
31
+ validate!
32
+
33
+ group_list = [
34
+ ui.color('Name', :bold),
35
+ ui.color('Protocol', :bold),
36
+ ui.color('From', :bold),
37
+ ui.color('To', :bold),
38
+ ui.color('CIDR', :bold),
39
+ ui.color('Description', :bold),
40
+ ]
41
+ connection.security_groups.sort_by(&:name).each do |group|
42
+ group.rules.each do |rule|
43
+ unless rule['ip_protocol'].nil?
44
+ group_list << group.name
45
+ group_list << rule['ip_protocol']
46
+ group_list << rule['from_port'].to_s
47
+ group_list << rule['to_port'].to_s
48
+ group_list << rule['ip_range']['cidr']
49
+ group_list << group.description
50
+ end
51
+ end
52
+ end
53
+ puts ui.list(group_list, :uneven_columns_across, 6)
54
+ end
55
+ end
56
+ end
57
+ end
@@ -1,7 +1,7 @@
1
1
  #
2
2
  # Author:: Seth Chisamore (<schisamo@opscode.com>)
3
3
  # Author:: Matt Ray (<matt@opscode.com>)
4
- # Copyright:: Copyright (c) 2011-2012 Opscode, Inc.
4
+ # Copyright:: Copyright (c) 2011-2013 Opscode, Inc.
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,6 +27,12 @@ class Chef
27
27
 
28
28
  banner "knife openstack image list (options)"
29
29
 
30
+ option :disable_filter,
31
+ :long => "--disable-filter",
32
+ :description => "Disable filtering of the image list. Currently filters names ending with 'initrd' or 'kernel'",
33
+ :boolean => true,
34
+ :default => false
35
+
30
36
  def run
31
37
 
32
38
  validate!
@@ -34,28 +40,31 @@ class Chef
34
40
  image_list = [
35
41
  ui.color('ID', :bold),
36
42
  ui.color('Name', :bold),
37
- # ui.color('Kernel ID', :bold),
38
- # ui.color('Architecture', :bold),
39
- # ui.color('Root Store', :bold),
40
- # ui.color('Location', :bold)
43
+ ui.color('Snapshot', :bold),
41
44
  ]
42
-
43
- connection.images.sort_by do |image|
44
- [image.name.downcase, image.id].compact
45
- end.each do |image|
46
- image_list << image.id
47
- image_list << image.name
48
- # image_list << image.kernel_id
49
- # image_list << image.architecture
50
- # image_list << image.root_device_type
51
- # image_list << image.location
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.id
52
+ image_list << image.name
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
60
+ end
61
+ end
62
+ rescue Excon::Errors::BadRequest => e
63
+ response = Chef::JSONCompat.from_json(e.response.body)
64
+ ui.fatal("Unknown server error (#{response['badRequest']['code']}): #{response['badRequest']['message']}")
65
+ raise e
52
66
  end
53
-
54
- image_list = image_list.map do |item|
55
- item.to_s
56
- end
57
-
58
- puts ui.list(image_list, :uneven_columns_across, 2)
67
+ puts ui.list(image_list, :uneven_columns_across, 3)
59
68
  end
60
69
  end
61
70
  end
@@ -1,7 +1,7 @@
1
1
  #
2
2
  # Author:: Seth Chisamore (<schisamo@opscode.com>)
3
3
  # Author:: Matt Ray (<matt@opscode.com>)
4
- # Copyright:: Copyright (c) 2011-2012 Opscode, Inc.
4
+ # Copyright:: Copyright (c) 2011-2013 Opscode, Inc.
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -49,12 +49,12 @@ class Chef
49
49
  :description => "The image ID for the server",
50
50
  :proc => Proc.new { |i| Chef::Config[:knife][:image] = i }
51
51
 
52
- # option :security_groups,
53
- # :short => "-G X,Y,Z",
54
- # :long => "--groups X,Y,Z",
55
- # :description => "The security groups for this server",
56
- # :default => ["default"],
57
- # :proc => Proc.new { |groups| groups.split(',') }
52
+ option :security_groups,
53
+ :short => "-G X,Y,Z",
54
+ :long => "--groups X,Y,Z",
55
+ :description => "The security groups for this server",
56
+ :default => ["default"],
57
+ :proc => Proc.new { |groups| groups.split(',') }
58
58
 
59
59
  option :chef_node_name,
60
60
  :short => "-N NAME",
@@ -62,11 +62,10 @@ class Chef
62
62
  :description => "The Chef node name for your new node"
63
63
 
64
64
  option :floating_ip,
65
- :short => "-a",
66
- :long => "--floating-ip",
67
- :boolean => true,
68
- :default => false,
69
- :description => "Request to associate a floating IP address to the new OpenStack node. Assumes IPs have been allocated to the project."
65
+ :short => "-a [IP]",
66
+ :long => "--floating-ip [IP]",
67
+ :default => "-1",
68
+ :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."
70
69
 
71
70
  option :private_network,
72
71
  :long => "--private-network",
@@ -148,7 +147,7 @@ class Chef
148
147
  rescue Errno::ECONNREFUSED
149
148
  sleep 2
150
149
  false
151
- rescue Errno::EHOSTUNREACH
150
+ rescue Errno::EHOSTUNREACH, Errno::ENETUNREACH
152
151
  sleep 2
153
152
  false
154
153
  rescue Errno::ENETUNREACH
@@ -163,14 +162,6 @@ class Chef
163
162
 
164
163
  validate!
165
164
 
166
- connection = Fog::Compute.new(
167
- :provider => 'OpenStack',
168
- :openstack_username => Chef::Config[:knife][:openstack_username],
169
- :openstack_api_key => Chef::Config[:knife][:openstack_password],
170
- :openstack_auth_url => Chef::Config[:knife][:openstack_auth_url],
171
- :openstack_tenant => Chef::Config[:knife][:openstack_tenant]
172
- )
173
-
174
165
  #servers require a name, generate one if not passed
175
166
  node_name = get_node_name(config[:chef_node_name])
176
167
 
@@ -178,25 +169,37 @@ class Chef
178
169
  :name => node_name,
179
170
  :image_ref => locate_config_value(:image),
180
171
  :flavor_ref => locate_config_value(:flavor),
181
- # :security_group => locate_config_value(:security_groups),
182
- :key_name => Chef::Config[:knife][:openstack_ssh_key_id],
183
- :personality => [{
184
- "path" => "/etc/chef/ohai/hints/openstack.json",
185
- "contents" => ''
186
- }]
172
+ :security_groups => locate_config_value(:security_groups),
173
+ :key_name => locate_config_value(:openstack_ssh_key_id)
187
174
  }
188
175
 
189
176
  Chef::Log.debug("Name #{node_name}")
190
177
  Chef::Log.debug("Image #{locate_config_value(:image)}")
191
178
  Chef::Log.debug("Flavor #{locate_config_value(:flavor)}")
192
- # Chef::Log.debug("Groups #{locate_config_value(:security_groups)}")
179
+ Chef::Log.debug("Requested Floating IP #{locate_config_value(:floating_ip)}")
180
+ Chef::Log.debug("Security Groups #{locate_config_value(:security_groups)}")
193
181
  Chef::Log.debug("Creating server #{server_def}")
194
- server = connection.servers.create(server_def)
182
+
183
+ begin
184
+ server = connection.servers.create(server_def)
185
+ rescue Excon::Errors::BadRequest => e
186
+ response = Chef::JSONCompat.from_json(e.response.body)
187
+ if response['badRequest']['code'] == 400
188
+ if response['badRequest']['message'] =~ /Invalid flavorRef/
189
+ ui.fatal("Bad request (400): Invalid flavor specified: #{server_def[:flavor_ref]}")
190
+ exit 1
191
+ else
192
+ ui.fatal("Bad request (400): #{response['badRequest']['message']}")
193
+ exit 1
194
+ end
195
+ else
196
+ ui.fatal("Unknown server error (#{response['badRequest']['code']}): #{response['badRequest']['message']}")
197
+ raise e
198
+ end
199
+ end
195
200
 
196
201
  msg_pair("Instance Name", server.name)
197
202
  msg_pair("Instance ID", server.id)
198
- # msg_pair("Security Groups", server.groups.join(", "))
199
- msg_pair("SSH Keypair", server.key_name)
200
203
 
201
204
  print "\n#{ui.color("Waiting for server", :magenta)}"
202
205
 
@@ -207,35 +210,44 @@ class Chef
207
210
 
208
211
  msg_pair("Flavor", server.flavor['id'])
209
212
  msg_pair("Image", server.image['id'])
210
- msg_pair("Public IP Address", server.public_ip_address['addr']) if server.public_ip_address
211
-
212
- if config[:floating_ip]
213
- associated = false
214
- connection.addresses.each do |address|
215
- if address.instance_id.nil?
216
- server.associate_address(address.ip)
217
- #a bit of a hack, but server.reload takes a long time
218
- server.addresses['public'].push({"version"=>4,"addr"=>address.ip})
219
- associated = true
220
- msg_pair("Floating IP Address", address.ip)
221
- break
213
+ msg_pair("SSH Identity File", config[:identity_file])
214
+ msg_pair("SSH Keypair", server.key_name) if server.key_name
215
+ msg_pair("SSH Password", server.password) if (server.password && !server.key_name)
216
+ Chef::Log.debug("Addresses #{server.addresses}")
217
+ msg_pair("Public IP Address", primary_public_ip_address(server.addresses)) if primary_public_ip_address(server.addresses)
218
+
219
+ floating_address = locate_config_value(:floating_ip)
220
+ Chef::Log.debug("Floating IP Address requested #{floating_address}")
221
+ unless (floating_address == '-1') #no floating IP requested
222
+ addresses = connection.addresses
223
+ #floating requested without value
224
+ if floating_address.nil?
225
+ free_floating = addresses.find_index {|a| a.fixed_ip.nil?}
226
+ if free_floating.nil? #no free floating IP found
227
+ ui.error("Unable to assign a Floating IP from allocated IPs.")
228
+ exit 1
229
+ else
230
+ floating_address = addresses[free_floating].ip
222
231
  end
223
232
  end
224
- unless associated
225
- ui.error("Unable to associate floating IP.")
226
- exit 1
227
- end
233
+ server.associate_address(floating_address)
234
+ #a bit of a hack, but server.reload takes a long time
235
+ (server.addresses['public'] ||= []) << {"version"=>4,"addr"=>floating_address}
236
+ msg_pair("Floating IP Address", floating_address)
228
237
  end
229
- Chef::Log.debug("Public IP Address actual #{server.public_ip_address['addr']}") if server.public_ip_address
230
238
 
231
- msg_pair("Private IP Address", server.private_ip_address['addr']) if server.private_ip_address
239
+ Chef::Log.debug("Addresses #{server.addresses}")
240
+ Chef::Log.debug("Public IP Address actual: #{primary_public_ip_address(server.addresses)}") if primary_public_ip_address(server.addresses)
241
+
242
+ msg_pair("Private IP Address", primary_private_ip_address(server.addresses)) if primary_private_ip_address(server.addresses)
232
243
 
233
244
  #which IP address to bootstrap
234
- bootstrap_ip_address = server.public_ip_address['addr'] if server.public_ip_address
245
+ bootstrap_ip_address = primary_public_ip_address(server.addresses) if primary_public_ip_address(server.addresses)
235
246
  if config[:private_network]
236
- bootstrap_ip_address = server.private_ip_address['addr']
247
+ bootstrap_ip_address = primary_private_ip_address(server.addresses)
237
248
  end
238
- Chef::Log.debug("Bootstrap IP Address #{bootstrap_ip_address}")
249
+
250
+ Chef::Log.debug("Bootstrap IP Address: #{bootstrap_ip_address}")
239
251
  if bootstrap_ip_address.nil?
240
252
  ui.error("No IP address available for bootstrapping.")
241
253
  exit 1
@@ -255,10 +267,10 @@ class Chef
255
267
  msg_pair("Instance ID", server.id)
256
268
  msg_pair("Flavor", server.flavor['id'])
257
269
  msg_pair("Image", server.image['id'])
258
- # msg_pair("Security Groups", server.groups.join(", "))
259
- msg_pair("SSH Keypair", server.key_name)
260
- msg_pair("Public IP Address", server.public_ip_address['addr']) if server.public_ip_address
261
- msg_pair("Private IP Address", server.private_ip_address['addr']) if server.private_ip_address
270
+ msg_pair("SSH Keypair", server.key_name) if server.key_name
271
+ msg_pair("SSH Password", server.password) if (server.password && !server.key_name)
272
+ msg_pair("Public IP Address", primary_public_ip_address(server.addresses)) if primary_public_ip_address(server.addresses)
273
+ msg_pair("Private IP Address", primary_private_ip_address(server.addresses)) if primary_private_ip_address(server.addresses)
262
274
  msg_pair("Environment", config[:environment] || '_default')
263
275
  msg_pair("Run List", config[:run_list].join(', '))
264
276
  end
@@ -268,6 +280,7 @@ class Chef
268
280
  bootstrap.name_args = [bootstrap_ip_address]
269
281
  bootstrap.config[:run_list] = config[:run_list]
270
282
  bootstrap.config[:ssh_user] = config[:ssh_user]
283
+ bootstrap.config[:ssh_password] = server.password
271
284
  bootstrap.config[:identity_file] = config[:identity_file]
272
285
  bootstrap.config[:host_key_verify] = config[:host_key_verify]
273
286
  bootstrap.config[:chef_node_name] = server.name
@@ -277,19 +290,58 @@ class Chef
277
290
  bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root'
278
291
  bootstrap.config[:template_file] = locate_config_value(:template_file)
279
292
  bootstrap.config[:environment] = config[:environment]
293
+ # let ohai know we're using OpenStack
294
+ Chef::Config[:knife][:hints] ||= {}
295
+ Chef::Config[:knife][:hints]['openstack'] ||= {}
280
296
  bootstrap
281
297
  end
282
298
 
283
- def ami
284
- @ami ||= connection.images.get(locate_config_value(:image))
299
+ def flavor
300
+ @flavor ||= connection.flavors.get(locate_config_value(:flavor))
301
+ end
302
+
303
+ def image
304
+ @image ||= connection.images.get(locate_config_value(:image))
305
+ end
306
+
307
+ def is_floating_ip_valid
308
+ address = locate_config_value(:floating_ip)
309
+ if address == '-1' #no floating IP requested
310
+ return true
311
+ end
312
+ addresses = connection.addresses
313
+ return false if addresses.empty? #no floating IPs
314
+ #floating requested without value
315
+ if address.nil?
316
+ if addresses.find_index {|a| a.fixed_ip.nil?}
317
+ return true
318
+ else
319
+ return false #no floating IPs available
320
+ end
321
+ end
322
+ #floating requested with value
323
+ if addresses.find_index {|a| a.ip == address}
324
+ return true
325
+ else
326
+ return false #requested floating IP does not exist
327
+ end
285
328
  end
286
329
 
287
330
  def validate!
331
+ super([:image, :openstack_username, :openstack_password, :openstack_auth_url])
332
+
333
+ if flavor.nil?
334
+ ui.error("You have not provided a valid flavor ID. Please note the options for this value are -f or --flavor.")
335
+ exit 1
336
+ end
288
337
 
289
- super([:image, :openstack_ssh_key_id, :openstack_username, :openstack_password, :openstack_auth_url])
338
+ if image.nil?
339
+ ui.error("You have not provided a valid image ID. Please note the options for this value are -I or --image.")
340
+ exit 1
341
+ end
290
342
 
291
- if ami.nil?
292
- ui.error("You have not provided a valid image ID. Please note the short option for this value recently changed from '-i' to '-I'.")
343
+ if !is_floating_ip_valid
344
+ ui.error("You have either requested an invalid floating IP address or none are available.")
293
345
  exit 1
294
346
  end
295
347
  end
@@ -1,7 +1,7 @@
1
1
  #
2
2
  # Author:: Seth Chisamore (<schisamo@opscode.com>)
3
3
  # Author:: Matt Ray (<matt@opscode.com>)
4
- # Copyright:: Copyright (c) 2011-2012 Opscode, Inc.
4
+ # Copyright:: Copyright (c) 2011-2013 Opscode, Inc.
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -66,15 +66,13 @@ class Chef
66
66
  begin
67
67
  server = connection.servers.get(instance_id)
68
68
 
69
- msg_pair("Instance ID", server.id)
70
69
  msg_pair("Instance Name", server.name)
70
+ msg_pair("Instance ID", server.id)
71
71
  msg_pair("Flavor", server.flavor['id'])
72
72
  msg_pair("Image", server.image['id'])
73
- # msg_pair("Availability Zone", server.availability_zone)
74
- # msg_pair("Security Groups", server.groups.join(", "))
75
- # msg_pair("SSH Key", server.key_name)
76
- msg_pair("Public IP Address", server.public_ip_address['addr']) if server.public_ip_address
77
- msg_pair("Private IP Address", server.private_ip_address['addr']) if server.private_ip_address
73
+ msg_pair("Public IP Address", primary_public_ip_address(server.addresses)) if primary_public_ip_address(server.addresses)
74
+ msg_pair("Private IP Address", primary_private_ip_address(server.addresses)) if primary_private_ip_address(server.addresses)
75
+
78
76
 
79
77
  puts "\n"
80
78
  confirm("Do you really want to delete this server")
@@ -93,6 +91,10 @@ class Chef
93
91
 
94
92
  rescue NoMethodError
95
93
  ui.error("Could not locate server '#{instance_id}'.")
94
+ rescue Excon::Errors::BadRequest => e
95
+ response = Chef::JSONCompat.from_json(e.response.body)
96
+ ui.fatal("Unknown server error (#{response['badRequest']['code']}): #{response['badRequest']['message']}")
97
+ raise e
96
98
  end
97
99
  end
98
100
  end
@@ -1,7 +1,7 @@
1
1
  #
2
2
  # Author:: Seth Chisamore (<schisamo@opscode.com>)
3
3
  # Author:: Matt Ray (<matt@opscode.com>)
4
- # Copyright:: Copyright (c) 2011-2012 Opscode, Inc.
4
+ # Copyright:: Copyright (c) 2011-2013 Opscode, Inc.
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -42,33 +42,40 @@ class Chef
42
42
  ui.color('Keypair', :bold),
43
43
  ui.color('State', :bold)
44
44
  ]
45
- connection.servers.all.sort_by(&:id).each do |server|
46
- server_list << server.id.to_s
47
- server_list << server.name
48
- if server.public_ip_address.nil?
49
- server_list << ''
50
- else
51
- server_list << server.public_ip_address['addr'].to_s
52
- end
53
- if server.private_ip_address.nil?
54
- server_list << ''
55
- else
56
- server_list << server.private_ip_address['addr'].to_s
57
- end
58
- server_list << server.flavor['id'].to_s
59
- server_list << server.image['id'].to_s
60
- server_list << server.key_name
61
- server_list << begin
62
- state = server.state.to_s.downcase
63
- case state
64
- when 'shutting-down','terminated','stopping','stopped','error'
65
- ui.color(state, :red)
66
- when 'pending','build','paused','suspended','hard_reboot'
67
- ui.color(state, :yellow)
68
- else
69
- ui.color(state, :green)
45
+
46
+ begin
47
+ connection.servers.all.sort_by(&:id).each do |server|
48
+ server_list << server.id.to_s
49
+ server_list << server.name
50
+ if primary_public_ip_address(server.addresses)
51
+ server_list << primary_public_ip_address(server.addresses)
52
+ else
53
+ server_list << ''
54
+ end
55
+ if primary_private_ip_address(server.addresses)
56
+ server_list << primary_private_ip_address(server.addresses)
57
+ else
58
+ server_list << ''
59
+ end
60
+ server_list << server.flavor['id'].to_s
61
+ server_list << server.image['id'].to_s
62
+ server_list << server.key_name
63
+ server_list << begin
64
+ state = server.state.to_s.downcase
65
+ case state
66
+ when 'shutting-down','terminated','stopping','stopped','error','shutoff'
67
+ ui.color(state, :red)
68
+ when 'pending','build','paused','suspended','hard_reboot'
69
+ ui.color(state, :yellow)
70
+ else
71
+ ui.color(state, :green)
72
+ end
70
73
  end
71
- end
74
+ end
75
+ rescue Excon::Errors::BadRequest => e
76
+ response = Chef::JSONCompat.from_json(e.response.body)
77
+ ui.fatal("Unknown server error (#{response['badRequest']['code']}): #{response['badRequest']['message']}")
78
+ raise e
72
79
  end
73
80
  puts ui.list(server_list, :uneven_columns_across, 8)
74
81
 
@@ -76,5 +83,3 @@ class Chef
76
83
  end
77
84
  end
78
85
  end
79
-
80
-
@@ -1,6 +1,6 @@
1
1
  module Knife
2
2
  module OpenStack
3
- VERSION = "0.6.2"
3
+ VERSION = "0.7.0"
4
4
  MAJOR, MINOR, TINY = VERSION.split('.')
5
5
  end
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: knife-openstack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.7.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,24 +10,24 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-10-15 00:00:00.000000000 Z
13
+ date: 2013-03-12 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: fog
17
17
  requirement: !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
- - - ~>
20
+ - - ! '>='
21
21
  - !ruby/object:Gem::Version
22
- version: '1.4'
22
+ version: 1.10.0
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  none: false
27
27
  requirements:
28
- - - ~>
28
+ - - ! '>='
29
29
  - !ruby/object:Gem::Version
30
- version: '1.4'
30
+ version: 1.10.0
31
31
  - !ruby/object:Gem::Dependency
32
32
  name: chef
33
33
  requirement: !ruby/object:Gem::Requirement
@@ -63,6 +63,7 @@ files:
63
63
  - knife-openstack.gemspec
64
64
  - lib/chef/knife/openstack_base.rb
65
65
  - lib/chef/knife/openstack_flavor_list.rb
66
+ - lib/chef/knife/openstack_group_list.rb
66
67
  - lib/chef/knife/openstack_image_list.rb
67
68
  - lib/chef/knife/openstack_server_create.rb
68
69
  - lib/chef/knife/openstack_server_delete.rb
@@ -88,9 +89,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
88
89
  version: '0'
89
90
  requirements: []
90
91
  rubyforge_project:
91
- rubygems_version: 1.8.23
92
+ rubygems_version: 1.8.24
92
93
  signing_key:
93
94
  specification_version: 3
94
95
  summary: OpenStack Compute Support for Chef's Knife Command
95
96
  test_files: []
96
- has_rdoc: true