On 05/05/2011 12:41 AM, Mo Morsi wrote:
Introduces a generic web puppet resource type to access various web apis from puppet. Implements a provider of this type using curl to issue the requests.
Then uses this provider to issue requests to the conductor api to create providers and hardware profiles, analyzing the results for errors
recipes/aeolus_recipe/aeolus_recipe.pp | 12 +-- .../aeolus_recipe/lib/puppet/provider/web/curl.rb | 114 ++++++++++++++++++++ recipes/aeolus_recipe/lib/puppet/type/web.rb | 49 +++++++++ recipes/aeolus_recipe/manifests/aeolus.pp | 4 +- recipes/aeolus_recipe/manifests/conductor.pp | 98 +++++++---------- recipes/aeolus_recipe/manifests/defaults.pp | 13 +++ 6 files changed, 219 insertions(+), 71 deletions(-) create mode 100644 recipes/aeolus_recipe/lib/puppet/provider/web/curl.rb create mode 100644 recipes/aeolus_recipe/lib/puppet/type/web.rb
diff --git a/recipes/aeolus_recipe/aeolus_recipe.pp b/recipes/aeolus_recipe/aeolus_recipe.pp index 879b314..8574584 100644 --- a/recipes/aeolus_recipe/aeolus_recipe.pp +++ b/recipes/aeolus_recipe/aeolus_recipe.pp @@ -33,33 +33,27 @@ include aeolus::iwhd
aeolus::create_bucket{"aeolus":}
-aeolus::site_admin{"admin": +aeolus::site_admin{"$admin_user": email => 'dcuser@aeolusproject.org',
password => 'password',
password => "$admin_password", first_name => 'aeolus', last_name => 'user'}
aeolus::provider{"mock": type => 'mock', port => 3002,
login_user => 'admin',
login_password => 'password', require => Aeolus::Site_admin["admin"] }
aeolus::provider{"ec2-us-east-1": type => 'ec2', endpoint => 'us-east-1', port => 3003,
login_user => 'admin',
login_password => 'password', require => Aeolus::Site_admin["admin"] }
aeolus::provider{"ec2-us-west-1": type => 'ec2', endpoint => 'us-west-1', port => 3004,
login_user => 'admin',
login_password => 'password', require => Aeolus::Site_admin["admin"] }
aeolus::conductor::hwp{"hwp1":
@@ -67,7 +61,5 @@ aeolus::conductor::hwp{"hwp1": cpu => "1", storage => "1", architecture => "x86_64",
login_user => 'admin',
login_password => 'password', require => Aeolus::Site_admin["admin"] }
diff --git a/recipes/aeolus_recipe/lib/puppet/provider/web/curl.rb b/recipes/aeolus_recipe/lib/puppet/provider/web/curl.rb new file mode 100644 index 0000000..b5f4468 --- /dev/null +++ b/recipes/aeolus_recipe/lib/puppet/provider/web/curl.rb @@ -0,0 +1,114 @@ +require 'curb' +require 'uuid' +require 'fileutils'
+# Helper to invoke the web request w/ curl +def web_request(method, uri, request_params, params = {})
- raise Puppet::Error, "Must specify http method and uri" if method.nil? || uri.nil?
- curl = Curl::Easy.new
- if params.has_key?(:cookie)
- curl.enable_cookies = true
- curl.cookiefile = params[:cookie]
- curl.cookiejar = params[:cookie]
- end
- curl.follow_location = (params.has_key?(:follow)&& params[:follow])
- case(method)
- when 'get'
- url = uri
- url += ";" + request_params.collect { |k,v| "#{k}=#{v}" }.join("&") unless request_params.nil?
- curl.url = url
- curl.http_get
- return curl
- when 'post'
- cparams = []
- request_params.each_pair { |k,v| cparams<< Curl::PostField.content(k,v) } unless request_params.nil?
- curl.url = uri
- curl.http_post(cparams)
- return curl
- #when 'put'
- #when 'delete'
- end
+end
+# Helper to verify the response +def verify_result(result, verify = {})
- returns = (verify.has_key?(:returns)&& !verify[:returns].nil?) ? verify[:returns] : "200"
- returns = [returns] unless returns.is_a? Array
- unless returns.include?(result.response_code.to_s)
- raise Puppet::Error, "Invalid HTTP Return Code: #{result.response_code},
was expecting one of #{returns.join(", ")}"
- end
- if verify.has_key?(:body)&& !verify[:body].nil?&& !(result.body_str =~ Regexp.new(verify[:body]))
- raise Puppet::Error, "Expecting #{verify[:body]} in the result"
- end
+end
+# Puppet provider definition +Puppet::Type.type(:web).provide :curl do
- desc "Use curl to access web resources"
- def http_method
- @request_method
- end
- def http_method=(request_method)
- begin
# Set request method and generate a unique session key
@request_method = request_method
session = "/tmp/#{UUID.new.generate}"
# Invoke a login request if necessary
if @resource[:login]
login_params = @resource[:login].reject { |k,v| ['http_method', 'uri'].include?(k) }
web_request(@resource[:login]['http_method'], @resource[:login]['uri'],
login_params, :cookie => session, :follow => @resource[:follow]).close
end
# Check to see if we should actually run the request
skip_request = !@resource[:unless].nil?
if @resource[:unless]
result = web_request(@resource[:unless]['http_method'], @resource[:unless]['uri'],
@resource[:unless]['parameters'],
:cookie => session, :follow => @resource[:follow])
begin
verify_result(result,
:returns => @resource[:unless]['returns'],
:body => @resource[:unless]['verify'])
rescue Puppet::Error => e
skip_request = false
end
result.close
end
return if skip_request
# Actually run the request and verify the result
uri = !@resource[:uri].nil? ? @resource[:uri] : @resource[:name]
result = web_request(request_method, uri, @resource[:parameters],
:cookie => session, :follow => @resource[:follow])
verify_result(result,
:returns => @resource[:returns],
:body => @resource[:verify])
result.close
# Invoke a logout request if necessary
if @resource[:logout]
logout_params = @resource[:login].reject { |k,v| ['http_method', 'uri'].include?(k) }
web_request(@resource[:logout]['http_method'], @resource[:logout]['uri'],
logout_params, :cookie => session, :follow => @resource[:follow]).close
end
- rescue Exception => e
raise Puppet::Error, "An exception was raised when invoking web request: #{e}"
- ensure
FileUtils.rm_f(session) if @resource[:logout]
- end
- end
+end diff --git a/recipes/aeolus_recipe/lib/puppet/type/web.rb b/recipes/aeolus_recipe/lib/puppet/type/web.rb new file mode 100644 index 0000000..a9bf765 --- /dev/null +++ b/recipes/aeolus_recipe/lib/puppet/type/web.rb @@ -0,0 +1,49 @@ +Puppet::Type.newtype(:web) do
- @doc = "Issue a request via the world wide web"
- newparam :name
- newparam(:uri) do
desc "URI to the web resource"
- end
- newproperty(:http_method) do
desc "HTTP method through which to issue the web request"
validate do |value|
unless ['post', 'get', 'put', 'delete'].include?(value)
raise ArgumentError, "#{value} is not a valid HTTP request method"
end
end
- end
- newparam(:parameters) do
desc "Hash of parameters to include in the web request"
- end
- newparam(:returns) do
desc "Expected http return codes of the request"
defaultto "200"
# TODO validate value(s) is among possible valid http return codes
- end
- newparam(:follow) do
desc "Boolean indicating if redirects should be followed"
newvalues(:true, :false)
- end
- newparam(:verify) do
desc "String to verify as being part of the result"
- end
- newparam(:login) do
desc "Login parameters to be used if a login is required before making the request"
- end
- newparam(:logout) do
desc "Logout parameters to be used if a logout is requred after making the request"
- end
- newparam(:unless) do
desc "Do not run request if the request specified here succeeds"
- end
+end diff --git a/recipes/aeolus_recipe/manifests/aeolus.pp b/recipes/aeolus_recipe/manifests/aeolus.pp index ca684bb..a399771 100644 --- a/recipes/aeolus_recipe/manifests/aeolus.pp +++ b/recipes/aeolus_recipe/manifests/aeolus.pp @@ -54,13 +54,11 @@ class aeolus { }
# Create a new provider in aeolus -define aeolus::provider($type, $port, $endpoint="", $login_user="", $login_password=""){ +define aeolus::provider($type, $port, $endpoint=""){ aeolus::deltacloud{$name: provider_type => $type, endpoint => $endpoint, port => $port} aeolus::conductor::provider{$name: type => $type, url => "http://localhost:$%7Bport%7D/api",
login_user => $login_user,
}login_password => $login_password, require => Aeolus::Deltacloud[$name] }
diff --git a/recipes/aeolus_recipe/manifests/conductor.pp b/recipes/aeolus_recipe/manifests/conductor.pp index fee834f..7312e3a 100644 --- a/recipes/aeolus_recipe/manifests/conductor.pp +++ b/recipes/aeolus_recipe/manifests/conductor.pp @@ -228,66 +228,48 @@ define aeolus::site_admin($email="", $password="", $first_name="", $last_name="" require => Exec[create_site_admin_user]} }
-define aeolus::conductor::login($user,$password){
- exec{"conductor-login-for-${name}":
command => "/usr/bin/curl -X POST http://localhost/conductor/user_session \
-d user_session[login]=${user} \
-d user_session[password]=${password} \
-d commit=submit \
-c /tmp/aeolus-${user}.cookie \
--location --post301 --post302 -k -f",
onlyif => "/usr/bin/test ! -f /tmp/aeolus-${user}.cookie || \"\" == \"`curl -X GET http://localhost/conductor -b /tmp/aeolus-${user}.cookie -i --silent | grep 'HTTP/1.1 200'`\"",
require => Service['aeolus-conductor', 'httpd']}
-}
-define aeolus::conductor::logout($user){
- exec{"conductor-logout-for-${name}":
command => "/usr/bin/curl -X GET http://localhost/conductor/logout -b /tmp/aeolus-${user}.cookie --location -k -f",
onlyif => "/usr/bin/test -f /tmp/aeolus-${user}.cookie" } # TODO add condition ensuring cookie / session is valid
- exec{"conductor-logout-cookie-for-${name}":
command => "/bin/rm /tmp/aeolus-${user}.cookie",
onlyif => "/usr/bin/test -f /tmp/aeolus-${user}.cookie",
require => Exec["conductor-logout-for-${name}"]}
-}
- # Create a new provider via the conductor
-define aeolus::conductor::provider($type="",$url="",$login_user="",$login_password=""){
- aeolus::conductor::login{"provider-${name}": user => $login_user, password => $login_password }
- exec{"add-conductor-provider-${name}":
command => "/usr/bin/curl -X POST http://localhost/conductor/admin/providers \
-b /tmp/aeolus-${login_user}.cookie \
-d provider[name]=${name} \
-d provider[url]=${url} \
-d provider[provider_type_codename]=${type} \
--location --post301 --post302 -k -f",
logoutput => true,
require => [Aeolus::Conductor::Login["provider-$name"]] }
- aeolus::conductor::logout{"provider-${name}":
user => $login_user,
require => Exec["add-conductor-provider-${name}"] }
+define aeolus::conductor::provider($type="",$url=""){
- web{ "provider-$name":
- uri => "https://localhost/conductor/admin/providers",
- http_method => 'post',
- parameters => { 'provider[name]' => $name, 'provider[url]' => $url,
'provider[provider_type_codename]' => $type },
- returns => '200',
- verify => '.*Provider added.*',
- follow => true,
- unless => { 'http_method' => 'get',
'uri' => 'https://localhost/conductor/admin/providers',
'verify' => ".*$name.*" },
- require => Service['aeolus-conductor']
- } }
-define aeolus::conductor::hwp($memory='', $cpu='', $storage='', $architecture='', $login_user="",$login_password=""){
- aeolus::conductor::login{"hwp-${name}": user => $login_user, password => $login_password }
- exec{"add-conductor-hwp-${name}":
command => "/usr/bin/curl -X POST http://localhost/conductor/admin/hardware_profiles \
-b /tmp/aeolus-${login_user}.cookie \
-d hardware_profile[name]=${name} \
-d hardware_profile[memory_attributes][value]=${memory} \
-d hardware_profile[cpu_attributes][value]=${cpu} \
-d hardware_profile[storage_attributes][value]=${storage} \
-d hardware_profile[architecture_attributes][value]=${architecture} \
-d hardware_profile[memory_attributes][name]=memory -d hardware_profile[memory_attributes][unit]=MB \
-d hardware_profile[cpu_attributes][name]=cpu -d hardware_profile[cpu_attributes][unit]=count \
-d hardware_profile[storage_attributes][name]=storage -d hardware_profile[storage_attributes][unit]=GB \
-d hardware_profile[architecture_attributes][name]=architecture -d hardware_profile[architecture_attributes][unit]=label \
-d commit=Save \
--location --post301 --post302 -k -f",
logoutput => true,
require => [Aeolus::Conductor::Login["hwp-$name"]] }
- aeolus::conductor::logout{"hwp-${name}":
user => $login_user,
require => Exec["add-conductor-hwp-${name}"] }
+define aeolus::conductor::hwp($memory='', $cpu='', $storage='', $architecture=''){
- web{ "hwp-$name":
- uri => "https://localhost/conductor/admin/hardware_profiles",
- http_method => 'post',
- parameters => {'hardware_profile[name]' => $name,
'hardware_profile[memory_attributes][value]' => $memory,
'hardware_profile[cpu_attributes][value]' => $cpu,
'hardware_profile[storage_attributes][value]' => $storage,
'hardware_profile[architecture_attributes][value]' => $architecture,
'hardware_profile[memory_attributes][name]' => 'memory',
'hardware_profile[memory_attributes][unit]' => 'MB',
'hardware_profile[cpu_attributes][name]' => 'cpu',
'hardware_profile[cpu_attributes][unit]' => 'count',
'hardware_profile[storage_attributes][name]' => 'storage',
'hardware_profile[storage_attributes][unit]' => 'GB',
'hardware_profile[architecture_attributes][name]' => 'architecture',
'hardware_profile[architecture_attributes][unit]' => 'label',
'commit' => 'Save'},
- returns => '200',
- #verify => '.*Hardware profile added.*',
- follow => true,
- unless => { 'http_method' => 'get',
'uri' => 'https://localhost/conductor/admin/hardware_profiles',
'verify' => ".*$name.*" },
- require => Service['aeolus-conductor']
- } }
diff --git a/recipes/aeolus_recipe/manifests/defaults.pp b/recipes/aeolus_recipe/manifests/defaults.pp index f5c74c3..57b79f6 100644 --- a/recipes/aeolus_recipe/manifests/defaults.pp +++ b/recipes/aeolus_recipe/manifests/defaults.pp @@ -8,3 +8,16 @@
Package {provider => 'rpm'}
+$admin_user='admin' +$admin_password='password'
+# Setup the default login/logout targets for web requests +Web{
- login => { 'http_method' => 'post',
'uri' => 'https://localhost/conductor/user_session',
'user_session[login]' => "$admin_user",
'user_session[password]' => "$admin_password",
'commit' => 'submit' },
- logout => { 'http_method' => 'post',
'uri' => 'https://localhost/conductor/logout' }
+}
I think you are missing the Requires found in the attached patch.
M