knife-rackspace 0.6.2 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.chef/knife.rb +18 -0
- data/.gitignore +2 -0
- data/.travis.yml +28 -0
- data/CHANGELOG.md +12 -0
- data/Gemfile +4 -0
- data/README.rdoc +17 -3
- data/Rakefile +30 -0
- data/knife-rackspace.gemspec +7 -0
- data/lib/chef/knife/rackspace_base.rb +52 -29
- data/lib/chef/knife/rackspace_network_create.rb +46 -0
- data/lib/chef/knife/rackspace_network_delete.rb +37 -0
- data/lib/chef/knife/rackspace_network_list.rb +31 -0
- data/lib/chef/knife/rackspace_server_create.rb +193 -15
- data/lib/knife-rackspace/version.rb +1 -1
- data/spec/cassettes/v1/should_list_images.yml +123 -0
- data/spec/cassettes/v1/should_list_server_flavors.yml +462 -0
- data/spec/cassettes/v2/should_list_images.yml +293 -0
- data/spec/cassettes/v2/should_list_server_flavors.yml +361 -0
- data/spec/integration/integration_spec.rb +90 -0
- data/spec/integration_spec_helper.rb +134 -0
- data/spec/spec_helper.rb +3 -0
- metadata +144 -62
data/.chef/knife.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
current_dir = File.dirname(__FILE__)
|
2
|
+
log_level :info
|
3
|
+
log_location STDOUT
|
4
|
+
#node_name "knife-rackspace"
|
5
|
+
#client_key "#{current_dir}/knife-rackspace.pem"
|
6
|
+
#validation_client_name "knife-rackspace-validator"
|
7
|
+
#validation_key "#{current_dir}/knife-rackspace-validator.pem"
|
8
|
+
#chef_server_url "https://api.opscode.com/organizations/knife-rackspace"
|
9
|
+
#cache_type 'basicfile'
|
10
|
+
#cache_options( :path => "#{ENV['home']}/.chef/checksums" )
|
11
|
+
#cookbook_path ["#{current_dir}/../cookbooks"]
|
12
|
+
|
13
|
+
knife[:rackspace_api_username] = "#{ENV['OS_USERNAME']}"
|
14
|
+
knife[:rackspace_api_key] = "#{ENV['OS_PASSWORD']}"
|
15
|
+
|
16
|
+
#https_proxy 'https://localhost:8888'
|
17
|
+
#knife[:ssl_verify_peer] = false
|
18
|
+
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
---
|
2
|
+
language: ruby
|
3
|
+
rvm:
|
4
|
+
- 2.0.0
|
5
|
+
- 1.9.3
|
6
|
+
- 1.8.7
|
7
|
+
env:
|
8
|
+
global:
|
9
|
+
- secure: ! 'eEsikE/p2yz7/cFvCN/NOoljxBqjzsTWnKC7iZ+fEGGsyhKVJgWn4AasBusZ
|
10
|
+
|
11
|
+
mhtQcOqwakRulzVc+EZN4pqWHgaMC7SnSwhqRU5u1E+BPI4iWNsu+7rjiXhE
|
12
|
+
|
13
|
+
cJK1vE8YodgsgRDQ6evZVQLkwJpRk0qq2tM2LDqpzvy2MeQJIGc='
|
14
|
+
- secure: ! 'V3ohIIF3I8lD05zTUs2nu+CcIzBYcvt1c1euRJ+SPcAH493b4cPquw1NZNFy
|
15
|
+
|
16
|
+
WDIW+1qDQi1DXr8C3Yq8bL4+pY66SukHtxyLhx0V26lGUvI8naq3IqageBQK
|
17
|
+
|
18
|
+
pkb2zZwVYO0a5mLEKOI/7omExBVHDxxX9Bw45vCHLKId3Wt21HE='
|
19
|
+
- secure: ! 'OyKQU1muVBijz2/EkLUgBWbwPKG2UqRhIfpB1ZFkfdQ/06+Vaf82ZEQk3DvY
|
20
|
+
|
21
|
+
2lYFrzvSN1yWrKTMU3XES/dPlVlH87LDOrRhNhgW/NeHhC5BzdwrtvBm08RN
|
22
|
+
|
23
|
+
64ACuFG8fch9zIbx2VTkyLV8xsFXPPSaKMbEXrnikLwqnl5M8PQ='
|
24
|
+
- secure: ! 'DoDVG0HwFg2a7Fj72tlxb8KJto+cBEh7J9YQZ35hK2cmeTsBAcZ4oSOUeAFg
|
25
|
+
|
26
|
+
s2MvBhX0man0zjYPStbL1vcpuS1Ecea3kGzefG0lYhHAzvK7wqqRqhKNFsnd
|
27
|
+
|
28
|
+
FPEVgZrcj3/Lc7mlkbigZouDM7BJLQbMGOv/KOS6f3nYzIhkOfE='
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
## v0.7.0
|
2
|
+
* KNIFE_RACKSPACE-32 Ensure hint file is created to improve Ohai detection.
|
3
|
+
* KNIFE-181 correct mixed use of 'rackspace_auth_url' and 'rackspace_api_auth_url'. Only 'rackspace_auth_url' is correct.
|
4
|
+
* KNIFE-182 default to Rackspace Open Cloud (v2)
|
5
|
+
* KNIFE-267 Rackspace server create with networks
|
6
|
+
* KNIFE-271 Enable winrm authentication on knife-rackspace
|
7
|
+
* KNIFE-281 pass https_proxy and http_proxy setting onto fog; added ssl_verify_peer setting to disable certificate validation
|
8
|
+
* KNIFE-282 Add the ability to inject files on server creation
|
9
|
+
* KNIFE-289 Add Integration Tests
|
10
|
+
|
11
|
+
* KNOWN ISSUES: KNIFE-296 knife-windows overrides -x option with winrm-user
|
12
|
+
|
1
13
|
## v0.6.2
|
2
14
|
* bump release to fix permission issues inside the gem
|
3
15
|
|
data/Gemfile
CHANGED
data/README.rdoc
CHANGED
@@ -33,13 +33,13 @@ You also have the option of passing your Rackspace API Username/Key into the ind
|
|
33
33
|
# provision a new 1GB Ubuntu 10.04 webserver
|
34
34
|
knife rackspace server create -I 112 -f 3 -A 'Your Rackspace API username' -K "Your Rackspace API Key" -r 'role[webserver]'
|
35
35
|
|
36
|
-
To select for the
|
36
|
+
To select for the previous Rackspace API (aka 'v1'), you can use the <tt>--rackspace-version v1</tt> command option. 'v2' is the default, so if you're still using exclusively 'v1' you will probably want to add the following to your <tt>knife.rb</tt>:
|
37
37
|
|
38
|
-
knife[:rackspace_version] = '
|
38
|
+
knife[:rackspace_version] = 'v1'
|
39
39
|
|
40
40
|
This plugin also has support for authenticating against an alternate API Auth URL. This is useful if you are a Rackspace Cloud UK user, here is an example of configuring your <tt>knife.rb</tt>:
|
41
41
|
|
42
|
-
knife[:
|
42
|
+
knife[:rackspace_auth_url] = "lon.auth.api.rackspacecloud.com"
|
43
43
|
|
44
44
|
This plugin also has support for specifying which region to create servers into:
|
45
45
|
|
@@ -50,6 +50,14 @@ valid options include:
|
|
50
50
|
ORD_ENDPOINT = 'https://ord.servers.api.rackspacecloud.com/v2'
|
51
51
|
LON_ENDPOINT = 'https://lon.servers.api.rackspacecloud.com/v2'
|
52
52
|
|
53
|
+
If you are behind a proxy you can specify it in the knife.rb file as follows:
|
54
|
+
|
55
|
+
https_proxy https://PROXY_IP_ADDRESS:PORT
|
56
|
+
|
57
|
+
SSL certificate verification can be disabled by include the following in your knife.rb file:
|
58
|
+
|
59
|
+
knife[:ssl_verify_peer] = false
|
60
|
+
|
53
61
|
Additionally the following options may be set in your `knife.rb`:
|
54
62
|
|
55
63
|
* flavor
|
@@ -67,6 +75,12 @@ Provisions a new server in the Rackspace Cloud and then perform a Chef bootstrap
|
|
67
75
|
|
68
76
|
If no name is provided, nodes created with the v1 API are named after their instance ID, with the v2 API they are given a random 'rs-XXXXXXXXX' name.
|
69
77
|
|
78
|
+
Files can be injected onto the provisioned system using the <tt>--file</tt> switch. For example to inject <tt>my_script.sh</tt> into <tt>/root/initialize.sh</tt> you would use the following switch
|
79
|
+
|
80
|
+
--file /root/initialize.sh=my_script.sh.
|
81
|
+
|
82
|
+
Note: You can only inject text files and the maximum destination path is 255 characters.
|
83
|
+
|
70
84
|
== knife rackspace server delete
|
71
85
|
|
72
86
|
Deletes an existing server in the currently configured Rackspace Cloud account by the server/instance id. You can find the instance id by entering 'knife rackspace server list'. Please note - this does not delete the associated node and client objects from the Chef server unless you pass the <tt>-P</tt> or <tt>--purge</tt> command option. Using the <tt>--purge</tt> option with v2 nodes will attempt to delete the node and client by the name of the node.
|
data/Rakefile
CHANGED
@@ -19,3 +19,33 @@
|
|
19
19
|
|
20
20
|
require 'bundler'
|
21
21
|
Bundler::GemHelper.install_tasks
|
22
|
+
|
23
|
+
require 'rspec/core/rake_task'
|
24
|
+
RSpec::Core::RakeTask.new(:spec)
|
25
|
+
task :default => [:credentials, :spec, 'integration:live']
|
26
|
+
|
27
|
+
task :credentials do
|
28
|
+
if ENV['TRAVIS_SECURE_ENV_VARS'] == 'false'
|
29
|
+
puts "Setting vars"
|
30
|
+
ENV['OS_USERNAME'] = '_RAX_USERNAME_'
|
31
|
+
ENV['OS_PASSWORD'] = '_RAX_PASSWORD_'
|
32
|
+
ENV['RS_TENANT_ID'] = '000000'
|
33
|
+
ENV['RS_CDN_TENANT_NAME'] = '_CDN-TENANT-NAME_'
|
34
|
+
end
|
35
|
+
fail "Not all required variables detected" unless ENV['OS_USERNAME'] && ENV['OS_PASSWORD'] && ENV['RS_CDN_TENANT_NAME'] && ENV['RS_TENANT_ID']
|
36
|
+
end
|
37
|
+
|
38
|
+
namespace :integration do
|
39
|
+
desc 'Run the integration tests'
|
40
|
+
RSpec::Core::RakeTask.new(:test) do |t|
|
41
|
+
t.pattern = 'spec/integration/**'
|
42
|
+
end
|
43
|
+
|
44
|
+
desc 'Run the integration tests live (no VCR cassettes)'
|
45
|
+
task :live do
|
46
|
+
unless ENV['TRAVIS'] == 'true' && ENV['TRAVIS_SECURE_ENV_VARS'] == 'false'
|
47
|
+
ENV['INTEGRATION_TESTS'] = 'live'
|
48
|
+
Rake::Task['integration:test'].invoke
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/knife-rackspace.gemspec
CHANGED
@@ -17,7 +17,14 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
19
|
s.add_dependency "fog", "~> 1.6"
|
20
|
+
s.add_dependency "knife-windows"
|
20
21
|
s.add_dependency "chef", ">= 0.10.10"
|
21
22
|
s.require_paths = ["lib"]
|
22
23
|
|
24
|
+
# In Gemfile because I'm using a fork on Github. Hopefully pull request will be merged and a new gem will be released soon.
|
25
|
+
# s.add_development_dependency "knife-dsl"
|
26
|
+
s.add_development_dependency "rspec"
|
27
|
+
s.add_development_dependency "vcr"
|
28
|
+
s.add_development_dependency "ansi"
|
29
|
+
s.add_development_dependency "rake"
|
23
30
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
#
|
2
2
|
# Author:: Seth Chisamore (<schisamo@opscode.com>)
|
3
|
-
# Copyright:: Copyright (c) 2011 Opscode, Inc.
|
3
|
+
# Copyright:: Copyright (c) 2011-2013 Opscode, Inc.
|
4
4
|
# License:: Apache License, Version 2.0
|
5
5
|
#
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -50,57 +50,80 @@ class Chef
|
|
50
50
|
option :rackspace_version,
|
51
51
|
:long => '--rackspace-version VERSION',
|
52
52
|
:description => 'Rackspace Cloud Servers API version',
|
53
|
-
:default => "
|
53
|
+
:default => "v2",
|
54
54
|
:proc => Proc.new { |version| Chef::Config[:knife][:rackspace_version] = version }
|
55
55
|
|
56
|
-
option :
|
57
|
-
:long => "--rackspace-
|
56
|
+
option :rackspace_auth_url,
|
57
|
+
:long => "--rackspace-auth-url URL",
|
58
58
|
:description => "Your rackspace API auth url",
|
59
59
|
:default => "auth.api.rackspacecloud.com",
|
60
|
-
:proc => Proc.new { |url| Chef::Config[:knife][:
|
60
|
+
:proc => Proc.new { |url| Chef::Config[:knife][:rackspace_auth_url] = url }
|
61
61
|
|
62
62
|
option :rackspace_endpoint,
|
63
63
|
:long => "--rackspace-endpoint URL",
|
64
64
|
:description => "Your rackspace API endpoint",
|
65
65
|
:default => "https://dfw.servers.api.rackspacecloud.com/v2",
|
66
66
|
:proc => Proc.new { |url| Chef::Config[:knife][:rackspace_endpoint] = url }
|
67
|
+
|
68
|
+
option :file,
|
69
|
+
:long => '--file DESTINATION-PATH=SOURCE-PATH',
|
70
|
+
:description => 'File to inject on node',
|
71
|
+
:proc => Proc.new {|arg|
|
72
|
+
Chef::Config[:knife][:file] ||= []
|
73
|
+
Chef::Config[:knife][:file] << arg
|
74
|
+
}
|
67
75
|
end
|
68
76
|
end
|
69
77
|
|
70
78
|
def connection
|
71
|
-
Chef::Log.debug("version #{Chef::Config[:knife][:rackspace_version]}")
|
72
|
-
Chef::Log.debug("version #{config[:rackspace_version]}")
|
79
|
+
Chef::Log.debug("version #{Chef::Config[:knife][:rackspace_version]} (config)")
|
80
|
+
Chef::Log.debug("version #{config[:rackspace_version]} (cli)")
|
73
81
|
Chef::Log.debug("rackspace_api_key #{Chef::Config[:knife][:rackspace_api_key]}")
|
74
82
|
Chef::Log.debug("rackspace_username #{Chef::Config[:knife][:rackspace_username]}")
|
75
83
|
Chef::Log.debug("rackspace_api_username #{Chef::Config[:knife][:rackspace_api_username]}")
|
76
|
-
Chef::Log.debug("rackspace_auth_url #{Chef::Config[:knife][:rackspace_auth_url]}")
|
77
|
-
Chef::Log.debug("rackspace_auth_url #{config[:
|
78
|
-
Chef::Log.debug("rackspace_endpoint #{Chef::Config[:knife][:rackspace_endpoint]}")
|
79
|
-
Chef::Log.debug("rackspace_endpoint #{config[:rackspace_endpoint]}")
|
80
|
-
if (Chef::Config[:knife][:rackspace_version] == '
|
84
|
+
Chef::Log.debug("rackspace_auth_url #{Chef::Config[:knife][:rackspace_auth_url]} (config)")
|
85
|
+
Chef::Log.debug("rackspace_auth_url #{config[:rackspace_auth_url]} (cli)")
|
86
|
+
Chef::Log.debug("rackspace_endpoint #{Chef::Config[:knife][:rackspace_endpoint]} (config)")
|
87
|
+
Chef::Log.debug("rackspace_endpoint #{config[:rackspace_endpoint]} (cli)")
|
88
|
+
if (Chef::Config[:knife][:rackspace_version] == 'v1') || (config[:rackspace_version] == 'v1')
|
89
|
+
Chef::Log.debug("rackspace v1")
|
81
90
|
@connection ||= begin
|
82
|
-
connection = Fog::Compute.new(
|
83
|
-
:
|
84
|
-
|
85
|
-
:rackspace_api_key => Chef::Config[:knife][:rackspace_api_key],
|
86
|
-
:rackspace_username => (Chef::Config[:knife][:rackspace_username] || Chef::Config[:knife][:rackspace_api_username]),
|
87
|
-
:rackspace_auth_url => Chef::Config[:knife][:rackspace_api_auth_url] || config[:rackspace_api_auth_url],
|
88
|
-
:rackspace_endpoint => Chef::Config[:knife][:rackspace_endpoint] || config[:rackspace_endpoint]
|
89
|
-
)
|
91
|
+
connection = Fog::Compute.new(connection_params({
|
92
|
+
:version => 'v1'
|
93
|
+
}))
|
90
94
|
end
|
91
95
|
else
|
96
|
+
Chef::Log.debug("rackspace v2")
|
92
97
|
@connection ||= begin
|
93
|
-
connection = Fog::Compute.new(
|
94
|
-
:
|
95
|
-
:
|
96
|
-
|
97
|
-
:rackspace_username => (Chef::Config[:knife][:rackspace_username] || Chef::Config[:knife][:rackspace_api_username]),
|
98
|
-
:rackspace_auth_url => Chef::Config[:knife][:rackspace_api_auth_url] || config[:rackspace_api_auth_url]
|
99
|
-
)
|
98
|
+
connection = Fog::Compute.new(connection_params({
|
99
|
+
:version => 'v2',
|
100
|
+
:rackspace_endpoint => Chef::Config[:knife][:rackspace_endpoint] || config[:rackspace_endpoint]
|
101
|
+
}))
|
100
102
|
end
|
101
103
|
end
|
102
104
|
end
|
103
105
|
|
106
|
+
def connection_params(options={})
|
107
|
+
hash = options.merge({
|
108
|
+
:provider => 'Rackspace',
|
109
|
+
:rackspace_api_key => Chef::Config[:knife][:rackspace_api_key],
|
110
|
+
:rackspace_username => (Chef::Config[:knife][:rackspace_username] || Chef::Config[:knife][:rackspace_api_username]),
|
111
|
+
:rackspace_auth_url => Chef::Config[:knife][:rackspace_auth_url] || config[:rackspace_auth_url]
|
112
|
+
})
|
113
|
+
|
114
|
+
hash[:connection_options] ||= {}
|
115
|
+
Chef::Log.debug("https_proxy #{ Chef::Config[:https_proxy] || "<not specified>"} (config)")
|
116
|
+
Chef::Log.debug("http_proxy #{ Chef::Config[:http_proxy] || "<not specified>"} (config)")
|
117
|
+
if Chef::Config.has_key?(:https_proxy) || Chef::Config.has_key?(:http_proxy)
|
118
|
+
hash[:connection_options] = {:proxy => Chef::Config[:https_proxy] || Chef::Config[:http_proxy] }
|
119
|
+
end
|
120
|
+
Chef::Log.debug("using proxy #{hash[:connection_options][:proxy] || "<none>"} (config)")
|
121
|
+
Chef::Log.debug("ssl_verify_peer #{Chef::Config[:knife].include?(:ssl_verify_peer) ? Chef::Config[:knife][:ssl_verify_peer] : "<not specified>"} (config)")
|
122
|
+
hash[:connection_options][:ssl_verify_peer] = Chef::Config[:knife][:ssl_verify_peer] if Chef::Config[:knife].include?(:ssl_verify_peer)
|
123
|
+
|
124
|
+
hash
|
125
|
+
end
|
126
|
+
|
104
127
|
def locate_config_value(key)
|
105
128
|
key = key.to_sym
|
106
129
|
Chef::Config[:knife][key] || config[key]
|
@@ -134,7 +157,7 @@ class Chef
|
|
134
157
|
@public_dns_name ||= begin
|
135
158
|
Resolv.getname(ip_address)
|
136
159
|
rescue
|
137
|
-
"#{ip_address.gsub('.','-')}.static.cloud-ips.com"
|
160
|
+
"#{ip_address.gsub('.','-')}.static.cloud-ips.com" if ip_address
|
138
161
|
end
|
139
162
|
end
|
140
163
|
|
@@ -145,7 +168,7 @@ class Chef
|
|
145
168
|
end
|
146
169
|
|
147
170
|
def rackspace_api_version
|
148
|
-
version = Chef::Config[:knife][:rackspace_version] || '
|
171
|
+
version = Chef::Config[:knife][:rackspace_version] || 'v2'
|
149
172
|
version.downcase
|
150
173
|
end
|
151
174
|
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'chef/knife/rackspace_base'
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class RackspaceNetworkCreate < Knife
|
6
|
+
|
7
|
+
include Knife::RackspaceBase
|
8
|
+
|
9
|
+
banner "knife rackspace network create (options)"
|
10
|
+
|
11
|
+
option :label,
|
12
|
+
:short => "-L LABEL",
|
13
|
+
:long => "--label LABEL",
|
14
|
+
:description => "Label for the network",
|
15
|
+
:required => true
|
16
|
+
|
17
|
+
option :cidr,
|
18
|
+
:short => "-C CIDR",
|
19
|
+
:long => "--cidr CIDR",
|
20
|
+
:description => "CIDR for the network",
|
21
|
+
:required => true
|
22
|
+
|
23
|
+
def run
|
24
|
+
if version_one?
|
25
|
+
ui.error "Networks are not supported in v1"
|
26
|
+
exit 1
|
27
|
+
else
|
28
|
+
networks_list = [
|
29
|
+
ui.color('Label', :bold),
|
30
|
+
ui.color('CIDR', :bold),
|
31
|
+
ui.color('ID', :bold)
|
32
|
+
]
|
33
|
+
end
|
34
|
+
options = {}
|
35
|
+
[:cidr, :label].each do |key|
|
36
|
+
options[key] = config[key]
|
37
|
+
end
|
38
|
+
net = connection.networks.create(options)
|
39
|
+
|
40
|
+
msg_pair("Network ID", net.id)
|
41
|
+
msg_pair("Label", net.label)
|
42
|
+
msg_pair("CIDR", net.cidr)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'chef/knife/rackspace_base'
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class RackspaceNetworkDelete < Knife
|
6
|
+
|
7
|
+
include Knife::RackspaceBase
|
8
|
+
|
9
|
+
banner "knife rackspace network delete NETWORK_ID [NETWORK_ID] (options)"
|
10
|
+
|
11
|
+
def run
|
12
|
+
if version_one?
|
13
|
+
ui.error "Networks are not supported in v1"
|
14
|
+
exit 1
|
15
|
+
else
|
16
|
+
@name_args.each do |net_id|
|
17
|
+
network = connection.networks.get(net_id)
|
18
|
+
unless(network)
|
19
|
+
ui.error "Could not locate network: #{net_id}"
|
20
|
+
exit 1
|
21
|
+
end
|
22
|
+
msg_pair("Network ID", network.id)
|
23
|
+
msg_pair("Label", network.label)
|
24
|
+
msg_pair("CIDR", network.cidr)
|
25
|
+
|
26
|
+
puts "\n"
|
27
|
+
confirm("Do you really want to delete this network")
|
28
|
+
|
29
|
+
network.destroy
|
30
|
+
|
31
|
+
ui.warn("Deleted network #{network.id}")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'chef/knife/rackspace_base'
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class RackspaceNetworkList < Knife
|
6
|
+
|
7
|
+
include Knife::RackspaceBase
|
8
|
+
|
9
|
+
banner "knife rackspace network list (options)"
|
10
|
+
|
11
|
+
def run
|
12
|
+
if version_one?
|
13
|
+
ui.error "Networks are not supported in v1"
|
14
|
+
exit 1
|
15
|
+
else
|
16
|
+
networks_list = [
|
17
|
+
ui.color('Label', :bold),
|
18
|
+
ui.color('CIDR', :bold),
|
19
|
+
ui.color('ID', :bold)
|
20
|
+
]
|
21
|
+
end
|
22
|
+
connection.networks.sort_by(&:id).each do |network|
|
23
|
+
networks_list << network.label
|
24
|
+
networks_list << network.cidr
|
25
|
+
networks_list << network.id.to_s
|
26
|
+
end
|
27
|
+
puts ui.list(networks_list, :uneven_columns_across, 3)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -18,12 +18,16 @@
|
|
18
18
|
#
|
19
19
|
|
20
20
|
require 'chef/knife/rackspace_base'
|
21
|
+
require 'chef/knife/winrm_base'
|
22
|
+
require 'chef/knife'
|
21
23
|
|
22
24
|
class Chef
|
23
25
|
class Knife
|
24
26
|
class RackspaceServerCreate < Knife
|
25
27
|
|
26
28
|
include Knife::RackspaceBase
|
29
|
+
include Chef::Knife::WinrmBase
|
30
|
+
|
27
31
|
|
28
32
|
deps do
|
29
33
|
require 'fog'
|
@@ -35,6 +39,8 @@ class Chef
|
|
35
39
|
|
36
40
|
banner "knife rackspace server create (options)"
|
37
41
|
|
42
|
+
attr_accessor :initial_sleep_delay
|
43
|
+
|
38
44
|
option :flavor,
|
39
45
|
:short => "-f FLAVOR",
|
40
46
|
:long => "--flavor FLAVOR",
|
@@ -123,12 +129,60 @@ class Chef
|
|
123
129
|
:proc => Proc.new { |m| Chef::Config[:knife][:rackspace_metadata] = JSON.parse(m) },
|
124
130
|
:default => ""
|
125
131
|
|
132
|
+
option :hint,
|
133
|
+
:long => "--hint HINT_NAME[=HINT_FILE]",
|
134
|
+
:description => "Specify Ohai Hint to be set on the bootstrap target. Use multiple --hint options to specify multiple hints.",
|
135
|
+
:proc => Proc.new { |h|
|
136
|
+
Chef::Config[:knife][:hints] ||= {}
|
137
|
+
name, path = h.split("=")
|
138
|
+
Chef::Config[:knife][:hints][name] = path ? JSON.parse(::File.read(path)) : Hash.new
|
139
|
+
}
|
140
|
+
|
126
141
|
option :host_key_verify,
|
127
142
|
:long => "--[no-]host-key-verify",
|
128
143
|
:description => "Verify host key, enabled by default",
|
129
144
|
:boolean => true,
|
130
145
|
:default => true
|
131
146
|
|
147
|
+
option :default_networks,
|
148
|
+
:long => "--[no-]default-networks",
|
149
|
+
:description => "Include public and service networks, enabled by default",
|
150
|
+
:boolean => true,
|
151
|
+
:default => true
|
152
|
+
|
153
|
+
option :network,
|
154
|
+
:long => '--network [LABEL_OR_ID]',
|
155
|
+
:description => "Add private network. Use multiple --network options to specify multiple networks.",
|
156
|
+
:proc => Proc.new{ |name|
|
157
|
+
Chef::Config[:knife][:rackspace_networks] ||= []
|
158
|
+
(Chef::Config[:knife][:rackspace_networks] << name).uniq!
|
159
|
+
}
|
160
|
+
|
161
|
+
option :bootstrap_protocol,
|
162
|
+
:long => "--bootstrap-protocol protocol",
|
163
|
+
:description => "Protocol to bootstrap Windows servers. options: winrm",
|
164
|
+
:default => nil
|
165
|
+
|
166
|
+
option :server_create_timeout,
|
167
|
+
:long => "--server-create-timeout timeout",
|
168
|
+
:description => "How long to wait until the server is ready; default is 600 seconds",
|
169
|
+
:default => 600,
|
170
|
+
:proc => Proc.new { |v| Chef::Config[:knife][:server_create_timeouts] = v}
|
171
|
+
|
172
|
+
option :bootstrap_proxy,
|
173
|
+
:long => "--bootstrap-proxy PROXY_URL",
|
174
|
+
:description => "The proxy server for the node being bootstrapped",
|
175
|
+
:proc => Proc.new { |v| Chef::Config[:knife][:bootstrap_proxy] = v }
|
176
|
+
|
177
|
+
|
178
|
+
def load_winrm_deps
|
179
|
+
require 'winrm'
|
180
|
+
require 'em-winrm'
|
181
|
+
require 'chef/knife/bootstrap_windows_winrm'
|
182
|
+
require 'chef/knife/core/windows_bootstrap_context'
|
183
|
+
require 'chef/knife/winrm'
|
184
|
+
end
|
185
|
+
|
132
186
|
def tcp_test_ssh(hostname)
|
133
187
|
tcp_socket = TCPSocket.new(hostname, 22)
|
134
188
|
readable = IO.select([tcp_socket], nil, nil, 5)
|
@@ -153,6 +207,66 @@ class Chef
|
|
153
207
|
tcp_socket && tcp_socket.close
|
154
208
|
end
|
155
209
|
|
210
|
+
|
211
|
+
def parse_file_argument(arg)
|
212
|
+
dest, src = arg.split('=')
|
213
|
+
unless dest && src
|
214
|
+
ui.error "Unable to process file arguments #{arg}. The --file option requires both the destination on the remote machine as well as the local source be supplied using the form DESTINATION-PATH=SOURCE-PATH"
|
215
|
+
exit 1
|
216
|
+
end
|
217
|
+
[dest, src]
|
218
|
+
end
|
219
|
+
|
220
|
+
def encode_file(file)
|
221
|
+
begin
|
222
|
+
filename = File.expand_path(file)
|
223
|
+
content = File.read(filename)
|
224
|
+
rescue Errno::ENOENT => e
|
225
|
+
ui.error "Unable to read source file - #{filename}"
|
226
|
+
exit 1
|
227
|
+
end
|
228
|
+
Base64.encode64(content)
|
229
|
+
end
|
230
|
+
|
231
|
+
def files
|
232
|
+
return {} unless Chef::Config[:knife][:file]
|
233
|
+
|
234
|
+
files = []
|
235
|
+
Chef::Config[:knife][:file].each do |arg|
|
236
|
+
dest, src = parse_file_argument(arg)
|
237
|
+
Chef::Log.debug("Inject file #{src} into #{dest}")
|
238
|
+
files << {
|
239
|
+
:path => dest,
|
240
|
+
:contents => encode_file(src)
|
241
|
+
}
|
242
|
+
end
|
243
|
+
files
|
244
|
+
end
|
245
|
+
|
246
|
+
|
247
|
+
|
248
|
+
def tcp_test_winrm(hostname, port)
|
249
|
+
TCPSocket.new(hostname, port)
|
250
|
+
return true
|
251
|
+
rescue SocketError
|
252
|
+
sleep 2
|
253
|
+
false
|
254
|
+
rescue Errno::ETIMEDOUT
|
255
|
+
false
|
256
|
+
rescue Errno::EPERM
|
257
|
+
false
|
258
|
+
rescue Errno::ECONNREFUSED
|
259
|
+
sleep 2
|
260
|
+
false
|
261
|
+
rescue Errno::EHOSTUNREACH
|
262
|
+
sleep 2
|
263
|
+
false
|
264
|
+
rescue Errno::ENETUNREACH
|
265
|
+
sleep 2
|
266
|
+
false
|
267
|
+
end
|
268
|
+
|
269
|
+
|
156
270
|
def run
|
157
271
|
$stdout.sync = true
|
158
272
|
|
@@ -160,15 +274,24 @@ class Chef
|
|
160
274
|
ui.error("You have not provided a valid image value. Please note the short option for this value recently changed from '-i' to '-I'.")
|
161
275
|
exit 1
|
162
276
|
end
|
163
|
-
|
277
|
+
|
278
|
+
if locate_config_value(:bootstrap_protocol) == 'winrm'
|
279
|
+
load_winrm_deps
|
280
|
+
end
|
281
|
+
|
164
282
|
node_name = get_node_name(config[:chef_node_name] || config[:server_name])
|
283
|
+
networks = get_networks(Chef::Config[:knife][:rackspace_networks])
|
165
284
|
|
166
|
-
server = connection.servers.
|
285
|
+
server = connection.servers.new(
|
167
286
|
:name => node_name,
|
168
287
|
:image_id => Chef::Config[:knife][:image],
|
169
288
|
:flavor_id => locate_config_value(:flavor),
|
170
|
-
:metadata => Chef::Config[:knife][:rackspace_metadata]
|
171
|
-
|
289
|
+
:metadata => Chef::Config[:knife][:rackspace_metadata],
|
290
|
+
:personality => files
|
291
|
+
)
|
292
|
+
server.save(
|
293
|
+
:networks => networks
|
294
|
+
)
|
172
295
|
|
173
296
|
msg_pair("Instance ID", server.id)
|
174
297
|
msg_pair("Host ID", server.host_id)
|
@@ -176,11 +299,14 @@ class Chef
|
|
176
299
|
msg_pair("Flavor", server.flavor.name)
|
177
300
|
msg_pair("Image", server.image.name)
|
178
301
|
msg_pair("Metadata", server.metadata)
|
302
|
+
if(networks && Chef::Config[:knife][:rackspace_networks])
|
303
|
+
msg_pair("Networks", Chef::Config[:knife][:rackspace_networks].sort.join(', '))
|
304
|
+
end
|
179
305
|
|
180
306
|
print "\n#{ui.color("Waiting server", :magenta)}"
|
181
|
-
|
307
|
+
|
308
|
+
server.wait_for(Integer(locate_config_value(:server_create_timeout))) { print "."; ready? }
|
182
309
|
# wait for it to be ready to do stuff
|
183
|
-
server.wait_for { print "."; ready? }
|
184
310
|
|
185
311
|
puts("\n")
|
186
312
|
|
@@ -188,9 +314,6 @@ class Chef
|
|
188
314
|
msg_pair("Public IP Address", public_ip(server))
|
189
315
|
msg_pair("Private IP Address", private_ip(server))
|
190
316
|
msg_pair("Password", server.password)
|
191
|
-
|
192
|
-
print "\n#{ui.color("Waiting for sshd", :magenta)}"
|
193
|
-
|
194
317
|
#which IP address to bootstrap
|
195
318
|
bootstrap_ip_address = public_ip(server)
|
196
319
|
if config[:private_network]
|
@@ -202,11 +325,18 @@ class Chef
|
|
202
325
|
exit 1
|
203
326
|
end
|
204
327
|
|
328
|
+
if locate_config_value(:bootstrap_protocol) == 'winrm'
|
329
|
+
print "\n#{ui.color("Waiting for winrm", :magenta)}"
|
330
|
+
print(".") until tcp_test_winrm(bootstrap_ip_address, locate_config_value(:winrm_port))
|
331
|
+
bootstrap_for_windows_node(server, bootstrap_ip_address).run
|
332
|
+
else
|
333
|
+
print "\n#{ui.color("Waiting for sshd", :magenta)}"
|
205
334
|
print(".") until tcp_test_ssh(bootstrap_ip_address) {
|
206
335
|
sleep @initial_sleep_delay ||= 10
|
207
336
|
puts("done")
|
208
337
|
}
|
209
338
|
bootstrap_for_node(server, bootstrap_ip_address).run
|
339
|
+
end
|
210
340
|
|
211
341
|
puts "\n"
|
212
342
|
msg_pair("Instance ID", server.id)
|
@@ -226,26 +356,45 @@ class Chef
|
|
226
356
|
def bootstrap_for_node(server, bootstrap_ip_address)
|
227
357
|
bootstrap = Chef::Knife::Bootstrap.new
|
228
358
|
bootstrap.name_args = [bootstrap_ip_address]
|
229
|
-
bootstrap.config[:run_list] = config[:run_list]
|
230
|
-
bootstrap.config[:first_boot_attributes] = config[:first_boot_attributes]
|
231
359
|
bootstrap.config[:ssh_user] = config[:ssh_user] || "root"
|
232
360
|
bootstrap.config[:ssh_password] = server.password
|
233
361
|
bootstrap.config[:identity_file] = config[:identity_file]
|
234
362
|
bootstrap.config[:host_key_verify] = config[:host_key_verify]
|
363
|
+
# bootstrap will run as root...sudo (by default) also messes up Ohai on CentOS boxes
|
364
|
+
bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root'
|
365
|
+
bootstrap_common_params(bootstrap, server)
|
366
|
+
end
|
367
|
+
|
368
|
+
def bootstrap_common_params(bootstrap, server)
|
369
|
+
bootstrap.config[:environment] = config[:environment]
|
370
|
+
bootstrap.config[:run_list] = config[:run_list]
|
235
371
|
if version_one?
|
236
372
|
bootstrap.config[:chef_node_name] = config[:chef_node_name] || server.id
|
237
373
|
else
|
238
|
-
bootstrap.config[:chef_node_name] = server.name
|
374
|
+
bootstrap.config[:chef_node_name] = config[:chef_node_name] || server.name
|
239
375
|
end
|
240
376
|
bootstrap.config[:prerelease] = config[:prerelease]
|
241
377
|
bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
|
242
378
|
bootstrap.config[:distro] = locate_config_value(:distro)
|
243
|
-
# bootstrap will run as root...sudo (by default) also messes up Ohai on CentOS boxes
|
244
|
-
bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root'
|
245
379
|
bootstrap.config[:template_file] = locate_config_value(:template_file)
|
246
|
-
bootstrap.config[:
|
380
|
+
bootstrap.config[:first_boot_attributes] = config[:first_boot_attributes]
|
381
|
+
bootstrap.config[:bootstrap_proxy] = locate_config_value(:bootstrap_proxy)
|
382
|
+
bootstrap.config[:encrypted_data_bag_secret] = config[:encrypted_data_bag_secret]
|
383
|
+
bootstrap.config[:encrypted_data_bag_secret_file] = config[:encrypted_data_bag_secret_file]
|
384
|
+
Chef::Config[:knife][:hints] ||= {}
|
385
|
+
Chef::Config[:knife][:hints]["rackspace"] ||= {}
|
247
386
|
bootstrap
|
248
387
|
end
|
388
|
+
|
389
|
+
def bootstrap_for_windows_node(server, bootstrap_ip_address)
|
390
|
+
bootstrap = Chef::Knife::BootstrapWindowsWinrm.new
|
391
|
+
bootstrap.name_args = [bootstrap_ip_address]
|
392
|
+
bootstrap.config[:winrm_user] = locate_config_value(:winrm_user) || 'Administrator'
|
393
|
+
bootstrap.config[:winrm_password] = locate_config_value(:winrm_password) || server.password
|
394
|
+
bootstrap.config[:winrm_transport] = locate_config_value(:winrm_transport)
|
395
|
+
bootstrap.config[:winrm_port] = locate_config_value(:winrm_port)
|
396
|
+
bootstrap_common_params(bootstrap, server)
|
397
|
+
end
|
249
398
|
|
250
399
|
end
|
251
400
|
#v2 servers require a name, random if chef_node_name is empty, empty if v1
|
@@ -254,5 +403,34 @@ class Chef
|
|
254
403
|
#lazy uuids
|
255
404
|
chef_node_name = "rs-"+rand.to_s.split('.')[1] unless version_one?
|
256
405
|
end
|
406
|
+
|
407
|
+
def get_networks(names)
|
408
|
+
names = Array(names)
|
409
|
+
if(Chef::Config[:knife][:rackspace_version] == 'v2')
|
410
|
+
if(config[:default_networks])
|
411
|
+
nets = [
|
412
|
+
'00000000-0000-0000-0000-000000000000',
|
413
|
+
'11111111-1111-1111-1111-111111111111'
|
414
|
+
]
|
415
|
+
else
|
416
|
+
nets = []
|
417
|
+
end
|
418
|
+
available_networks = connection.networks.all
|
419
|
+
|
420
|
+
names.each do |name|
|
421
|
+
net = available_networks.detect{|n| n.label == name || n.id == name}
|
422
|
+
if(net)
|
423
|
+
nets << net.id
|
424
|
+
else
|
425
|
+
ui.error("Failed to locate network: #{name}")
|
426
|
+
exit 1
|
427
|
+
end
|
428
|
+
end
|
429
|
+
nets
|
430
|
+
elsif(names && !names.empty?)
|
431
|
+
ui.error("Custom networks are only available in v2 API")
|
432
|
+
exit 1
|
433
|
+
end
|
434
|
+
end
|
257
435
|
end
|
258
436
|
end
|