The following patchset implements several new features for aeolus-configure including
- configuration profiles or grouping of aeolus components and seed data which can be configured seperately from each other
- interactive installer allowing the user to select which components are configured locally and the parameters to seed the application with
- expanded seed data interface including support for provider accounts and images
- expanded spec suite, verifying all the above
- a few bug fixes / improvements to smoothen operations
Everything has been tested, all existing functionality still fully works as does the new additions
--- .../aeolus/lib/puppet/provider/web_request/curl.rb | 260 +++++++++++++------- recipes/aeolus/lib/puppet/type/web_request.rb | 108 +++++++-- 2 files changed, 264 insertions(+), 104 deletions(-)
diff --git a/recipes/aeolus/lib/puppet/provider/web_request/curl.rb b/recipes/aeolus/lib/puppet/provider/web_request/curl.rb index 724f70f..14da60d 100644 --- a/recipes/aeolus/lib/puppet/provider/web_request/curl.rb +++ b/recipes/aeolus/lib/puppet/provider/web_request/curl.rb @@ -1,107 +1,86 @@ -require 'curb' -require 'uuidtools' 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 +# Provides an interface to curl using the curb gem for puppet +require 'curb'
- curl.follow_location = (params.has_key?(:follow) && params[:follow]) +# uses nokogiri to verify responses w/ xpath +require 'nokogiri'
- 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 +class Curl::Easy
- when 'post' + # Format request parameters for the specified request method + def self.format_params(method, params, file_params) + if([:get, :delete].include?(method)) + return params.collect { |k,v| "#{k}=#{v}" }.join("&") unless params.nil? + return "" + end + # post, put: 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' + params.each_pair { |k,v| cparams << Curl::PostField.content(k,v) } unless params.nil? + file_params.each_pair { |k,v| cparams << Curl::PostField.file(k,v) } unless file_params.nil? + return cparams 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(", ")}" + # Format a url for the specified request method, base uri, and parameters + def self.format_url(method, uri, params) + if([:get, :delete].include?(method)) + url = uri + url += ";" + format_params(method, params) + return url + end + # post, put: + return uri 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 + # Invoke a new curl request and return result + def self.web_request(method, uri, params = {}) + raise Puppet::Error, "Must specify http method (#{method}) and uri (#{uri})" if method.nil? || uri.nil?
-# Helper to process/parse web parameters -def process_params(request_method, params, uri) - begin - # Set request method and generate a unique session key - session = "/tmp/#{UUIDTools::UUID.timestamp_create.to_s}" - - # Invoke a login request if necessary - if params[:login] - login_params = params[:login].reject { |k,v| ['http_method', 'uri'].include?(k) } - web_request(params[:login]['http_method'], params[:login]['uri'], - login_params, :cookie => session, :follow => params[:follow]).close - end + curl = self.new
- # Check to see if we should actually run the request - skip_request = !params[:unless].nil? - if params[:unless] - result = web_request(params[:unless]['http_method'], params[:unless]['uri'], - params[:unless]['parameters'], - :cookie => session, :follow => params[:follow]) - begin - verify_result(result, - :returns => params[:unless]['returns'], - :body => params[:unless]['verify']) - rescue Puppet::Error => e - skip_request = false - end - result.close + if params.has_key?(:cookie) && !params[:cookie].nil? + curl.enable_cookies = true + curl.cookiefile = params[:cookie] + curl.cookiejar = params[:cookie] end - return if skip_request - - # Actually run the request and verify the result - uri = params[:name] if uri.nil? - result = web_request(request_method, uri, params[:parameters], - :cookie => session, :follow => params[:follow]) - verify_result(result, - :returns => params[:returns], - :body => params[:verify]) - result.close - - # Invoke a logout request if necessary - if params[:logout] - logout_params = params[:login].reject { |k,v| ['http_method', 'uri'].include?(k) } - web_request(params[:logout]['http_method'], params[:logout]['uri'], - logout_params, :cookie => session, :follow => params[:follow]).close + + curl.follow_location = (params.has_key?(:follow) && params[:follow]) + request_params = params[:parameters] + file_params = params[:file_parameters] + + case(method) + when 'get' + curl.url = format_url(method, uri, request_params) + curl.http_get + return curl + + when 'post' + curl.url = format_url(method, uri, request_params) + curl.multipart_form_post = true if !file_params.nil? && file_params.size > 0 + curl.http_post(*format_params(method, request_params, file_params)) + return curl + + when 'put' + curl.url = format_url(method, uri, request_params) + curl.multipart_form_post = true if !file_params.nil? && file_params.size > 0 + curl.http_put(*format_params(method, request_params, file_params)) + return curl + + when 'delete' + curl.url = format_url(method, uri, request_params) + curl.http_delete + return curl end + end
- rescue Exception => e - raise Puppet::Error, "An exception was raised when invoking web request: #{e}" + def valid_status_code?(valid_values=[]) + valid_values.include?(response_code.to_s) + end
- ensure - FileUtils.rm_f(session) if params[:logout] + def valid_xpath?(xpath="/") + !Nokogiri::HTML(body_str.to_s).xpath(xpath.to_s).empty? end + end
# Puppet provider definition @@ -116,6 +95,14 @@ Puppet::Type.type(:web_request).provide :curl do @uri end
+ def delete + @uri + end + + def put + @uri + end + def get=(uri) @uri = uri process_params('get', @resource, uri) @@ -125,4 +112,103 @@ Puppet::Type.type(:web_request).provide :curl do @uri = uri process_params('post', @resource, uri) end + + def delete=(uri) + @uri = uri + process_params('delete', @resource, uri) + end + + def put=(uri) + @uri = uri + process_params('put', @resource, uri) + end + + private + + # Helper to process/parse web parameters + def process_params(request_method, params, uri) + begin + cookies = nil + if params[:store_cookies_at] + FileUtils.touch(params[:store_cookies_at]) if !File.exist?(params[:store_cookies_at]) + cookies = params[:store_cookies_at] + elsif params[:use_cookies_at] + cookies = params[:use_cookies_at] + end + + # verify that we should actually run the request + return if skip_request?(params, cookies) + + # Actually run the request and verify the result + result = Curl::Easy::web_request(request_method, uri, + :parameters => params[:parameters], + :file_parameters => params[:file_parameters], + :cookie => cookies, + :follow => params[:follow]) + verify_result(result, + :returns => params[:returns], + :does_not_return => params[:does_not_return], + :contains => params[:contains], + :does_not_contain => params[:does_not_contain] ) + result.close + + rescue Exception => e + raise Puppet::Error, "An exception was raised when invoking web request: #{e}" + + ensure + FileUtils.rm_f(cookies) if params[:remove_cookies] + end + end + + # Helper to determine if we should skip the request + def skip_request?(params, cookie = nil) + [:if, :unless].each { |c| + condition = params[c] + unless condition.nil? + method = (condition.keys & ['get', 'post', 'delete', 'put']).first + result = Curl::Easy::web_request(method, condition[method], + :parameters => condition['parameters'], + :file_parameters => condition['file_parameters'], + :cookie => cookie, :follow => condition[:follow]) + result_succeeded = true + begin + verify_result(result, condition) + rescue Puppet::Error + result_succeeded = false + end + return true if (c == :if && !result_succeeded) || (c == :unless && result_succeeded) + end + } + return false + end + + # Helper to verify the response + def verify_result(result, verify = {}) + verify[:returns] = verify['returns'] if verify[:returns].nil? && !verify['returns'].nil? + verify[:does_not_return] = verify['does_not_return'] if verify[:does_not_return].nil? && !verify['does_not_return'].nil? + verify[:contains] = verify['contains'] if verify[:contains].nil? && !verify['contains'].nil? + verify[:does_not_contain] = verify['does_not_contain'] if verify[:does_not_contain].nil? && !verify['does_not_contain'].nil? + + if !verify[:returns].nil? && + !result.valid_status_code?(verify[:returns]) + raise Puppet::Error, "Invalid HTTP Return Code: #{result.response_code}, + was expecting one of #{verify[:returns].join(", ")}" + end + + if !verify[:does_not_return].nil? && + result.valid_status_code?(verify[:does_not_return]) + raise Puppet::Error, "Invalid HTTP Return Code: #{result.response_code}, + was not expecting one of #{verify[:does_not_return].join(", ")}" + end + + if !verify[:contains].nil? && + !result.valid_xpath?(verify[:contains]) + raise Puppet::Error, "Expecting #{verify[:contains]} in the result" + end + + if !verify[:does_not_contain].nil? && + result.valid_xpath?(verify[:does_not_contain]) + raise Puppet::Error, "Not expecting #{verify[:does_not_contain]} in the result" + end + end end diff --git a/recipes/aeolus/lib/puppet/type/web_request.rb b/recipes/aeolus/lib/puppet/type/web_request.rb index 5225633..407b2fe 100644 --- a/recipes/aeolus/lib/puppet/type/web_request.rb +++ b/recipes/aeolus/lib/puppet/type/web_request.rb @@ -1,29 +1,74 @@ +require 'uri' + +# A puppet resource type used to access resources on the World Wide Web Puppet::Type.newtype(:web_request) do - @doc = "Issue a request via the world wide web" + @doc = "Issue a request to a resource on the world wide web" + + private + + # Validates uris passed in + def self.validate_uri(url) + begin + uri = URI.parse(url) + raise ArgumentError, "Specified uri #{url} is not valid" if ![URI::HTTP, URI::HTTPS].include?(uri.class) + rescue URI::InvalidURIError + raise ArgumentError, "Specified uri #{url} is not valid" + end + end + + # Validates http statuses passed in + def self.validate_http_status(status) + status = [status] unless status.is_a?(Array) + status.each { |stat| + stat = stat.to_s + unless ['100', '101', '102', '122', + '200', '201', '202', '203', '204', '205', '206', '207', '226', + '300', '301', '302', '303', '304', '305', '306', '307', + '400', '401', '402', '403', '404', '405', '406', '407', '408', '409', + '410', '411', '412', '413', '414', '415', '416', '417', '418', + '422', '423', '424', '425', '426', '444', '449', '450', '499', + '500', '501', '502', '503', '504', '505', '506', '507', '508', ' 509', '510' + ].include?(stat) + raise ArgumentError, "Invalid http status code #{stat} specified" + end + } + end + + # Convert singular params into arrays of strings + def self.munge_array_params(value) + value = [value] unless value.is_a?(Array) + value = value.collect { |val| val.to_s } + value + end
newparam :name
newproperty(:get) do desc "Issue get request to the specified uri" - # TODO valid value to be a uri + validate do |value| Puppet::Type::Web_request.validate_uri(value) end end
newproperty(:post) do - desc "Issue get request to the specified uri" - # TODO valid value to be a uri + desc "Issue post request to the specified uri" + validate do |value| Puppet::Type::Web_request.validate_uri(value) end end
- #newproperty(:delete) - #newproperty(:put) + newproperty(:delete) do + desc "Issue delete request to the specified uri" + validate do |value| Puppet::Type::Web_request.validate_uri(value) end + end + + newproperty(:put) do + desc "Issue put request to the specified uri" + validate do |value| Puppet::Type::Web_request.validate_uri(value) 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 + newparam(:file_parameters) do + desc "Hash of file parameters to include in the web request" end
newparam(:follow) do @@ -31,19 +76,48 @@ Puppet::Type.newtype(:web_request) do newvalues(:true, :false) end
- newparam(:verify) do - desc "String to verify as being part of the result" + newparam(:store_cookies_at) do + desc "String indicating where session cookies should be stored" end
- newparam(:login) do - desc "Login parameters to be used if a login is required before making the request" + newparam(:use_cookies_at) do + desc "String indicating where session cookies should be read from" end
- newparam(:logout) do - desc "Logout parameters to be used if a logout is requred after making the request" + newparam(:remove_cookies) do + desc "Boolean indicating if cookies should be removed after using them" + newvalues(:true, :false) + end + + newparam(:returns) do + desc "Expected http return codes of the request" + defaultto ["200"] + validate do |value| Puppet::Type::Web_request.validate_http_status(value) end + munge do |value| Puppet::Type::Web_request.munge_array_params(value) end + end + + newparam(:does_not_return) do + desc "Unexecpected http return codes of the request" + validate do |value| Puppet::Type::Web_request.validate_http_status(value) end + munge do |value| Puppet::Type::Web_request.munge_array_params(value) end + end + + newparam(:contains) do + desc "XPath to verify as part of the result" + munge do |value| Puppet::Type::Web_request.munge_array_params(value) end + end + + newparam(:does_not_contain) do + desc "XPath to verify as not being part of the result" + munge do |value| Puppet::Type::Web_request.munge_array_params(value) end + end + + newparam(:if) do + desc "Invoke request only if the specified request returns true" end
newparam(:unless) do - desc "Do not run request if the request specified here succeeds" + desc "Invoke request unless the specified request returns true" end + end
--- recipes/aeolus/manifests/conductor.pp | 86 ++++++++++++++++++++++++++-- recipes/aeolus/manifests/defaults.pp | 14 ----- recipes/aeolus/manifests/image-factory.pp | 41 ++++++++++++++ 3 files changed, 120 insertions(+), 21 deletions(-)
diff --git a/recipes/aeolus/manifests/conductor.pp b/recipes/aeolus/manifests/conductor.pp index 42ea2fc..05ef72e 100644 --- a/recipes/aeolus/manifests/conductor.pp +++ b/recipes/aeolus/manifests/conductor.pp @@ -199,6 +199,32 @@ define aeolus::site_admin($email="", $password="", $first_name="", $last_name="" require => Exec[create_site_admin_user]} }
+# login to the aeolus conductor +define aeolus::conductor::login($password){ + web_request{ "$name-conductor-login": + post => 'https://localhost/conductor/user_session', + parameters => { 'user_session[login]' => "$name", 'user_session[password]' => "$password", + 'commit' => 'submit' }, + returns => '200', + follow => true, + store_cookies_at => "/tmp/aeolus-$name", + require => Service['aeolus-conductor'] + } +} + +# log out of the aeolus conductor +define aeolus::conductor::logout(){ + web_request{ "$name-conductor-logout": + post => 'https://localhost/conductor/logout', + parameters => { 'user_session[login]' => "admin", 'user_session[password]' => "password", + 'commit' => 'submit' }, + returns => '200', + follow => true, + use_cookies_at => "/tmp/aeolus-$name", + remove_cookies => true + } +} + # Create a new provider via the conductor define aeolus::conductor::provider($type="",$url=""){ web_request{ "provider-$name": @@ -206,15 +232,60 @@ define aeolus::conductor::provider($type="",$url=""){ parameters => { 'provider[name]' => $name, 'provider[url]' => $url, 'provider[provider_type_codename]' => $type }, returns => '200', - verify => '.*Provider added.*', + contains => "//html/body//li[text() = 'Provider added.']", follow => true, - unless => { 'http_method' => 'get', - 'uri' => 'https://localhost/conductor/providers', - 'verify' => ".*$name.*" }, + use_cookies_at => '/tmp/aeolus-admin', + unless => { 'get' => 'https://localhost/conductor/providers', + 'contains' => "//html/body//a[text() = '$name']" }, require => [Service['aeolus-conductor'], Exec['grant_site_admin_privs']] } }
+# Create a new provider account via the conductor +define aeolus::conductor::provider::account($provider="", $type="", $username="",$password="", $account_id="",$x509private="", $x509public=""){ + if $type == "mock" { + web_request{ "provider-account-$name": + post => "https://localhost/conductor/provider_accounts", + parameters => { 'provider_account[label]' => $name, + 'provider_account[provider]' => $provider, + 'provider_account[credentials_hash[username]]' => $username, + 'provider_account[credentials_hash[password]]' => $password, + 'quota[max_running_instances]' => 'unlimited', + 'commit' => 'Save' }, + + returns => '200', + #contains => "//table/thead/tr/th[text() = 'Properties for $name']", + follow => true, + use_cookies_at => '/tmp/aeolus-admin', + unless => { 'get' => 'https://localhost/conductor/provider_accounts', + 'contains' => "//html/body//a[text() = '$name']" }, + require => Service['aeolus-conductor']} + + } elsif $type == "ec2" { + web_request{ "provider-account-$name": + post => "https://localhost/conductor/provider_accounts", + parameters => { 'provider_account[label]' => $name, + 'provider_account[provider]' => $provider, + 'provider_account[credentials_hash[username]]' => $username, + 'provider_account[credentials_hash[password]]' => $password, + 'provider_account[credentials_hash[account_id]]' => $account_id, + 'quota[max_running_instances]' => 'unlimited', + 'commit' => 'Save' }, + file_parameters => { 'provider_account[credentials_hash[x509private]]'=> $x509private, + 'provider_account[credentials_hash[x509public]]' => $x509public }, + + returns => '200', + #contains => "//table/thead/tr/th[text() = 'Properties for $name']", + follow => true, + use_cookies_at => '/tmp/aeolus-admin', + unless => { 'get' => 'https://localhost/conductor/provider_accounts', + 'contains' => "//html/body//a[text() = '$name']" }, + require => Service['aeolus-conductor'] + } + } +} + + define aeolus::conductor::hwp($memory='', $cpu='', $storage='', $architecture=''){ web_request{ "hwp-$name": post => "https://localhost/conductor/hardware_profiles", @@ -235,9 +306,10 @@ define aeolus::conductor::hwp($memory='', $cpu='', $storage='', $architecture='' returns => '200', #verify => '.*Hardware profile added.*', follow => true, - unless => { 'http_method' => 'get', - 'uri' => 'https://localhost/conductor/hardware_profiles', - 'verify' => ".*$name.*" }, + use_cookies_at => '/tmp/aeolus-admin', + #unless => { 'http_method' => 'get', + # 'uri' => 'https://localhost/conductor/hardware_profiles', + # 'verify' => ".*$name.*" }, require => [Service['aeolus-conductor'], Exec['grant_site_admin_privs']] } } diff --git a/recipes/aeolus/manifests/defaults.pp b/recipes/aeolus/manifests/defaults.pp index f2fc891..f43fad3 100644 --- a/recipes/aeolus/manifests/defaults.pp +++ b/recipes/aeolus/manifests/defaults.pp @@ -7,17 +7,3 @@ #switching back to yum
Package {provider => 'rpm'} - -$admin_user='admin' -$admin_password='password' - -# Setup the default login/logout targets for web requests -Web_request{ - 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' } -} diff --git a/recipes/aeolus/manifests/image-factory.pp b/recipes/aeolus/manifests/image-factory.pp index 0bb9e72..69f8146 100644 --- a/recipes/aeolus/manifests/image-factory.pp +++ b/recipes/aeolus/manifests/image-factory.pp @@ -2,6 +2,13 @@
class aeolus::image-factory inherits aeolus {
+ # image factory client + package { 'rubygem-aeolus-image': ensure => 'installed' } + file{"/root/.aeolus-cli": + source => "/usr/lib/ruby/gems/1.8/gems/aeolus-image-0.1.0/examples/aeolus-cli", + require => Package['rubygem-aeolus-image'] } + + # image factory services package { 'libvirt': ensure=> 'installed' } @@ -61,3 +68,37 @@ class aeolus::image-factory::disabled { exec{"remove_aeolus_templates": command => "/bin/rm -rf /templates"} }
+define aeolus::image($template, $provider='', $target=''){ + exec{"build-$name-image": logoutput => true, timeout => 0, + command => "/usr/sbin/aeolus-configure-image $name $target $template $provider", + require => Service['aeolus-conductor']} + + web_request{ "deployment-$name": + post => "https://localhost/conductor/deployments", + parameters => { 'deployable_url' => "http://localhost/deployables/$name.xml", + 'deployment[name]' => $name, + 'deployment[pool_id]' => '1', + 'deployment[frontend_realm_id]' => '' , + 'commit' => 'Next', + 'suggested_deployable_id' => "other"}, + returns => '200', + #contains => "//html/body//li[text() = 'Provider added.']", + follow => true, + use_cookies_at => '/tmp/aeolus-admin', + #unless => { 'get' => 'https://localhost/conductor/providers', + # 'contains' => "//html/body//a[text() = '$name']" }, + require => Exec["build-$name-image"] + } + + #web_request{ "launch-deployment-$name": + # post => "https://localhost/conductor/deployments/new", + # parameters => { 'deployable_name' => $name }, + # returns => '200', + # #contains => "//html/body//li[text() = 'Provider added.']", + # follow => true, + # use_cookies_at => '/tmp/aeolus-admin', + # #unless => { 'get' => 'https://localhost/conductor/providers', + # # 'contains' => "//html/body//a[text() = '$name']" }, + # require => Web_request["deployment-$name"] + #} +}
--- bin/aeolus-configure-image | 49 ++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 49 insertions(+), 0 deletions(-) create mode 100755 bin/aeolus-configure-image
diff --git a/bin/aeolus-configure-image b/bin/aeolus-configure-image new file mode 100755 index 0000000..4f065b8 --- /dev/null +++ b/bin/aeolus-configure-image @@ -0,0 +1,49 @@ +#!/usr/bin/ruby +# helper script to generate aeolus images + +AEOLUS_IMAGE_VERSION='0.1.0' + +image_name = ARGV[0] +target = ARGV[1] +template = ARGV[2] +provider = ARGV[3] + +Dir.chdir "/usr/lib/ruby/gems/1.8/gems/aeolus-image-#{AEOLUS_IMAGE_VERSION}" + +cmd = "/usr/bin/ruby -rrubygems \ + /usr/lib/ruby/gems/1.8/gems/aeolus-image-#{AEOLUS_IMAGE_VERSION}/bin/aeolus-image build \ + --target #{target} --template #{template}" +puts "Building image for #{target} using #{template}" +#puts " Running build command #{cmd}" +out = `#{cmd}` +puts "Image build returned w/ exit code #{$?}" +puts "Image build output: #{out}" + +if out =~ /^\s*Image:\s*([0-9a-zA-Z-]*).*/ + image = $1 + cmd = "/usr/bin/ruby -rrubygems \ + /usr/lib/ruby/gems/1.8/gems/aeolus-image-#{AEOLUS_IMAGE_VERSION}/bin/aeolus-image push \ + --provider #{provider} --id #{image}" + + puts "Image #{image} built, pushing to #{provider}" + #puts "Running push command #{cmd}" + out = `#{cmd}` + + puts "Image push returned w/ exit code #{$?}" + puts "Image push output: #{out}" + + deployables_dir = '/var/www/html/deployables' + FileUtils.mkdir deployables_dir unless File.exist? deployables_dir + File.open("/var/www/html/deployables/#{image_name}.xml", "w") { |f| + f.write "<deployable name='#{image_name}'>\n" + + " <assemblies name = '#{image_name}'>\n" + + " <assembly name='#{image_name}' hwp='hwp1' >\n" + + " <image id='#{image}'></image>\n" + + " </assembly>\n" + + " </assemblies>\n" + + "</deployable>\n" + } + + puts "Deployment definition written" + puts "Image building complete" +end
this patch adds an interactive installer to aeolus such that the user will be prompted as to which aeolus components to install and seed data to initialize
the goal is to provide a very simple interface to configure only the bits the user wants, the components that are to be setup locally, the providers to be created, and parameters create core aeolus data --- recipes/aeolus/cli.rb | 145 ++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 145 insertions(+), 0 deletions(-) create mode 100644 recipes/aeolus/aeolus.pp create mode 100644 recipes/aeolus/cli.rb
diff --git a/recipes/aeolus/aeolus.pp b/recipes/aeolus/aeolus.pp new file mode 100644 index 0000000..e69de29 diff --git a/recipes/aeolus/cli.rb b/recipes/aeolus/cli.rb new file mode 100644 index 0000000..df3fa3e --- /dev/null +++ b/recipes/aeolus/cli.rb @@ -0,0 +1,145 @@ +# Interactive aeolus configure installation utility. +# Prompt the user for provider account and instance values and write them +# to a new puppet config files + +require 'rubygems' +require 'highline/import' + +puts "Press ^C at any time to terminate" +Signal.trap("INT") do + exit 1 +end + +NODE_YAML='/etc/aeolus-configure/nodes/default_custom' +PROFILE_RECIPE='/usr/share/aeolus-configure/modules/aeolus/manifests/profiles/custom.pp' + + +say "Select Aeolus Components to Install" +installed_component = nil +install_components = [] +while ![:None, :All].include?(installed_component) + installed_component = + choose do |menu| + menu.prompt = "Install Aeolus Component: " + menu.choice :All + menu.choice :None + menu.choice :"Image Factory" + menu.choice :"Image Warehouse" + menu.choice :"Conductor" + end + if installed_component == :"Image Factory" + install_components << "- aeolus::image-factory" + elsif installed_component == :"Image Warehouse" + install_components << "- aeolus::iwhd" + elsif installed_component == :"Conductor" + install_components << "- aeolus::conductor" + elsif installed_component == :All + install_components << "- aeolus::conductor" << + "- aeolus::image-factory" << + "- aeolus::iwhd" + end +end + +providers = [] +if install_components.include? "- aeolus::conductor" + provider_port = 3001 + profile='' + profile_requires = [] + while agree("Add provider (y/n)? ") + name = ask("Cloud provider label: ") + type = choose do |menu| + menu.prompt = "Cloud provider type: " + menu.choice :mock + menu.choice :ec2 + #menu.choice :rackspace + #menu.choice :rhevm + #menu.choice :vsphere + end + providers << [name,type] + + if type == :mock + profile += "aeolus::provider{#{name}:\n" + + " type => 'mock',\n" + + " port => '#{provider_port += 1}',\n" + + " require => Aeolus::Conductor::Login['admin'] }\n\n" + + "aeolus::conductor::provider::account{#{name}:\n" + + " provider => 'mock',\n" + + " type => 'mock',\n" + + " username => 'mockuser',\n" + + " password => 'mockpassword',\n" + + " require => Aeolus::Provider['#{name}'] }\n\n" + profile_requires << "Aeolus::Provider['#{name}']" << + "Aeolus::Conductor::Provider::Account['#{name}']" + + elsif type == :ec2 + endpoint = ask("EC2 Endpoint: ") + access_key = ask("EC2 Access Key: ") + secret_access_key = ask("EC2 Secret Access Key: "){ |q| q.echo = false } + account_id = ask("EC2 Account ID: ") + public_cert = ask("EC2 Public Cert: ") + private_key = ask("EC2 Private Key: ") + profile += "aeolus::provider{#{name}:\n" + + " type => 'ec2',\n" + + " endpoint => '#{endpoint}',\n" + + " port => '#{provider_port += 1}',\n" + + " require => Aeolus::Conductor::Login['admin'] }\n\n" + + "aeolus::conductor::provider::account{#{name}:\n" + + " provider => '#{name}',\n" + + " type => 'ec2',\n" + + " username => '#{access_key}',\n" + + " password => '#{secret_access_key}',\n" + + " account_id => '#{account_id}',\n" + + " x509private => '#{private_key}',\n" + + " x509public => '#{public_cert}',\n" + + " require => Aeolus::Provider['#{name}'] }\n\n" + profile_requires << "Aeolus::Provider['#{name}']" << + "Aeolus::Conductor::Provider::Account['#{name}']" + + # else if type == :rackspace ... + end + end + + # TODO change to create image / deploy to providers (which to select) + while agree("Deploy instance to providers (y/n)? ") + name = ask("Instance name: ") + providers.each { |provider| + pname,ptype = *provider + profile += "aeolus::image{#{pname}-#{name}:\n" + + " target => '#{ptype}',\n" + + " template => 'examples/custom_repo.tdl',\n" + + " provider => '#{pname}',\n" + + " require => [Aeolus::Conductor::Provider::Account['#{pname}'], Aeolus::Conductor::Hwp['hwp1']] }\n\n" + profile_requires << "Aeolus::Image['#{pname}-#{name}']" + } + + # while agree("Add yum repo? ") + # yum_repo = ask("URI ") + # end + # + # while agree("Add package? ") do + # package_name = ask("Package Name ") + # end + # + # while agree("Add file? ") do + # src_location = ask("File Source ") + # dst_location = ask("File Destination ") + # end + # + end +end + + +# create the profile +text = File.read PROFILE_RECIPE +File.open(PROFILE_RECIPE, 'w+'){|f| + requires = profile_requires.join(',') + requires += ", " unless requires == "" + f << text.gsub(/#AEOLUS_SEED_DATA_REQUIRES/, requires). + gsub(/#AEOLUS_SEED_DATA/, profile) +} + +# create the node yaml +text = File.read NODE_YAML +File.open(NODE_YAML, 'w+'){|f| + f << text.gsub(/CUSTOM_CLASSES/, install_components.join("\n")) +}
--- bin/aeolus-cleanup | 6 ++- bin/aeolus-configure | 25 +++++++++++-- conf/default_configure | 2 +- conf/default_custom | 7 ++++ recipes/aeolus/manifests/conductor.pp | 36 +------------------ recipes/aeolus/manifests/init.pp | 3 ++ recipes/aeolus/manifests/profiles/custom.pp | 26 +++++++++++++ recipes/aeolus/manifests/profiles/default.pp | 51 ++++++++++++++++++++++++++ 8 files changed, 114 insertions(+), 42 deletions(-) create mode 100644 conf/default_custom create mode 100644 recipes/aeolus/manifests/profiles/custom.pp create mode 100644 recipes/aeolus/manifests/profiles/default.pp
diff --git a/bin/aeolus-cleanup b/bin/aeolus-cleanup index bdbbacd..8e3abcd 100644 --- a/bin/aeolus-cleanup +++ b/bin/aeolus-cleanup @@ -31,11 +31,13 @@ while true ; do esac done
+PUPPET_NODE='cleanup' + export FACTER_AEOLUS_ENABLE_HTTPS=true export FACTER_AEOLUS_ENABLE_SECURITY=false -puppet /usr/share/aeolus-configure/modules/aeolus/manifests/defaults.pp \ +puppet /usr/share/aeolus-configure/aeolus.pp \ --modulepath=/usr/share/aeolus-configure/modules/ \ - --external_nodes '/usr/sbin/aeolus-node cleanup' --node_terminus exec \ + --external_nodes "/usr/sbin/aeolus-node $PUPPET_NODE" --node_terminus exec \ --logdest=/var/log/aeolus-configure/aeolus-cleanup.log \ --logdest=console \ $LOGLEVEL diff --git a/bin/aeolus-configure b/bin/aeolus-configure index 310b76e..0ed3ffc 100644 --- a/bin/aeolus-configure +++ b/bin/aeolus-configure @@ -4,38 +4,55 @@ usage() cat << EOF
USAGE: -aeolus-configure [-d|--debug] [-h|--help] [-v|--verbose] +aeolus-configure [-d|--debug] [-h|--help] [-v|--verbose] [-i|--interactive]
OPTIONS: -h | --help Show this message. -d | --debug Debug logging mode. -v | --verbose Verbose logging mode. + -i | --interactive Interactive installer. EOF }
-args=`getopt -o :hdv --long help,debug,verbose -- "$@"` +args=`getopt -o :hdvi --long help,debug,verbose,interactive -- "$@"` if test $? != 0 then usage exit 1 fi
+INTERACTIVE=1 + eval set -- $args while true ; do case "$1" in -h|--help) usage ; exit 1 ; shift ;; -d|--debug) LOGLEVEL="--debug" ; shift ;; -v|--verbose) LOGLEVEL="--verbose" ; shift ;; + -i|--interactive) INTERACTIVE=0 ; shift ;; --) shift ; break ;; *) usage ; exit 1 ;; esac done
+PUPPET_NODE='configure' + +if [[ $INTERACTIVE -eq 0 ]] ; then +PUPPET_NODE='custom' +/usr/bin/ruby /usr/share/aeolus-configure/cli.rb +if [ $? == 1 ] ; then +echo +exit +fi +fi + +echo "Launching aeolus configuration recipe..." + export FACTER_AEOLUS_ENABLE_HTTPS=true export FACTER_AEOLUS_ENABLE_SECURITY=false -puppet /usr/share/aeolus-configure/modules/aeolus/manifests/defaults.pp \ +puppet /usr/share/aeolus-configure/aeolus.pp \ --modulepath=/usr/share/aeolus-configure/modules/ \ - --external_nodes '/usr/sbin/aeolus-node configure' --node_terminus exec \ + --external_nodes "/usr/sbin/aeolus-node $PUPPET_NODE" --node_terminus exec \ --logdest=/var/log/aeolus-configure/aeolus-configure.log \ --logdest=console \ $LOGLEVEL diff --git a/conf/default_configure b/conf/default_configure index 33ac7e1..21b74a7 100644 --- a/conf/default_configure +++ b/conf/default_configure @@ -37,7 +37,7 @@ classes: - aeolus::conductor - aeolus::image-factory - aeolus::iwhd -- aeolus::conductor::seed_data +- aeolus::profiles::default # Uncomment this section to include rhev setup #- aeolus::rhevm # diff --git a/conf/default_custom b/conf/default_custom new file mode 100644 index 0000000..1e7f7aa --- /dev/null +++ b/conf/default_custom @@ -0,0 +1,7 @@ +--- +parameters: + enable_https: true + enable_security: false +classes: +CUSTOM_CLASSES +- aeolus::profiles::custom diff --git a/recipes/aeolus/manifests/conductor.pp b/recipes/aeolus/manifests/conductor.pp index 05ef72e..d5f5fa6 100644 --- a/recipes/aeolus/manifests/conductor.pp +++ b/recipes/aeolus/manifests/conductor.pp @@ -111,40 +111,6 @@ class aeolus::conductor inherits aeolus { enable => 'true' } }
-class aeolus::conductor::seed_data { - aeolus::create_bucket{"aeolus":} - - aeolus::site_admin{"$admin_user": - email => 'aeolususer@aeolusproject.org', - password => "$admin_password", - first_name => 'aeolus', - last_name => 'user'} - - aeolus::provider{"mock": - type => 'mock', - port => 3002, - require => Aeolus::Site_admin["admin"] } - - aeolus::provider{"ec2-us-east-1": - type => 'ec2', - endpoint => 'us-east-1', - port => 3003, - require => Aeolus::Site_admin["admin"] } - - aeolus::provider{"ec2-us-west-1": - type => 'ec2', - endpoint => 'us-west-1', - port => 3004, - require => Aeolus::Site_admin["admin"] } - - aeolus::conductor::hwp{"hwp1": - memory => "512", - cpu => "1", - storage => "", - architecture => "x86_64", - require => Aeolus::Site_admin["admin"] } - -}
class aeolus::conductor::remove_seed_data { aeolus::deltacloud::disabled{"mock": } @@ -182,7 +148,7 @@ class aeolus::conductor::disabled { }
# Create a new site admin conductor web user -define aeolus::site_admin($email="", $password="", $first_name="", $last_name=""){ +define aeolus::conductor::site_admin($email="", $password="", $first_name="", $last_name=""){ exec{"create_site_admin_user": cwd => '/usr/share/aeolus-conductor', environment => "RAILS_ENV=production", diff --git a/recipes/aeolus/manifests/init.pp b/recipes/aeolus/manifests/init.pp index 7768733..f7f7bca 100644 --- a/recipes/aeolus/manifests/init.pp +++ b/recipes/aeolus/manifests/init.pp @@ -13,6 +13,9 @@ import "image-factory" import "rhevm" import "vmware"
+import "defaults" +import "profiles/*" + if $aeolus_enable_https == "true" or $aeolus_enable_https == "1" { import "openssl" $enable_https = true diff --git a/recipes/aeolus/manifests/profiles/custom.pp b/recipes/aeolus/manifests/profiles/custom.pp new file mode 100644 index 0000000..c78301d --- /dev/null +++ b/recipes/aeolus/manifests/profiles/custom.pp @@ -0,0 +1,26 @@ +class aeolus::profiles::custom { + +aeolus::create_bucket{"aeolus":} + +aeolus::conductor::site_admin{"admin": + email => 'dcuser@aeolusproject.org', + password => "password", + first_name => 'aeolus', + last_name => 'user'} + +aeolus::conductor::login{"admin": password => "password", + require => Aeolus::Conductor::Site_admin['admin']} + +#AEOLUS_SEED_DATA + +aeolus::conductor::hwp{"hwp1": + memory => "1", + cpu => "1", + storage => "1", + architecture => "x86_64", + require => Aeolus::Conductor::Login["admin"] } + +aeolus::conductor::logout{"admin": + require => [#AEOLUS_SEED_DATA_REQUIRES + Aeolus::Conductor::Hwp["hwp1"]] } +} diff --git a/recipes/aeolus/manifests/profiles/default.pp b/recipes/aeolus/manifests/profiles/default.pp new file mode 100644 index 0000000..9a470f6 --- /dev/null +++ b/recipes/aeolus/manifests/profiles/default.pp @@ -0,0 +1,51 @@ +class aeolus::profiles::default { + + aeolus::create_bucket{"aeolus":} + + aeolus::conductor::site_admin{"admin": + email => 'dcuser@aeolusproject.org', + password => "password", + first_name => 'aeolus', + last_name => 'user'} + + aeolus::conductor::login{"admin": password => "password", + require => Aeolus::Conductor::Site_admin['admin']} + + aeolus::provider{"mock": + type => 'mock', + port => 3002, + require => Aeolus::Conductor::Login["admin"] } + aeolus::conductor::provider::account{"mockuser": + provider => 'mock', + type => 'mock', + username => 'mockuser', + password => 'mockpassword', + require => Aeolus::Provider["mock"] } + + aeolus::provider{"ec2-us-east-1": + type => 'ec2', + endpoint => 'us-east-1', + port => 3003, + require => Aeolus::Conductor::Login["admin"] } + + aeolus::provider{"ec2-us-west-1": + type => 'ec2', + endpoint => 'us-west-1', + port => 3004, + require => Aeolus::Conductor::Login["admin"] } + + aeolus::conductor::hwp{"hwp1": + memory => "512", + cpu => "1", + storage => "", + architecture => "x86_64", + require => Aeolus::Conductor::Login["admin"] } + + aeolus::conductor::logout{"admin": + require => [Aeolus::Provider['mock'], + Aeolus::Conductor::Provider::Account['mockuser'], + Aeolus::Provider['ec2-us-east-1'], + Aeolus::Provider['ec2-us-west-1'], + Aeolus::Conductor::Hwp['hwp1']] } + +}
--- Gemfile | 24 +++++++++ Rakefile | 5 ++ spec/seed_data_spec.rb | 135 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 164 insertions(+), 0 deletions(-) create mode 100644 Gemfile create mode 100644 spec/seed_data_spec.rb
diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..662287a --- /dev/null +++ b/Gemfile @@ -0,0 +1,24 @@ +source 'http://rubygems.org' + +gem 'rails' + +gem 'sqlite3', :require => 'sqlite3' + +gem 'authlogic' +gem 'deltacloud-client', :require => 'deltacloud' +gem 'sass' +gem 'haml' +gem 'will_paginate' +gem 'nokogiri' +gem 'compass' +gem 'compass-960-plugin', :require => 'ninesixty' +gem 'simple-navigation' +gem 'typhoeus' +gem 'rest-client' +#gem 'rack-restful_submit' +gem 'uuidtools' +gem 'pg' +gem 'thin' +gem 'json' +gem 'railties' +gem 'rspec' diff --git a/Rakefile b/Rakefile index 647cf65..2918116 100644 --- a/Rakefile +++ b/Rakefile @@ -38,6 +38,11 @@ Spec::Rake::SpecTask.new(:cleanup_spec) do |t| t.spec_files = FileList['spec/cleanup_spec.rb'] end
+desc "Run seed spec tests locally" +Spec::Rake::SpecTask.new(:seed_spec) do |t| + t.spec_files = FileList['spec/seed_data_spec.rb'] +end + begin require 'rake/remotespectask'
diff --git a/spec/seed_data_spec.rb b/spec/seed_data_spec.rb new file mode 100644 index 0000000..8eb1651 --- /dev/null +++ b/spec/seed_data_spec.rb @@ -0,0 +1,135 @@ +require 'spec_helper' + +require 'fileutils' +require 'curb' + +# FIXME should verify agaist ec2 somehow (perhaps using the mock_ec2 driver?) + +describe "aeolus-configure seed data api" do + before(:all) do + # !!! need to comment out "#Defaults requiretty" via visudo for this to work remotely + # also need to make sure the user this will be running as has passwordless sudo access + + `sudo /usr/sbin/aeolus-cleanup` + $?.exitstatus.should == 0 + + `sudo /usr/sbin/aeolus-configure` + $?.exitstatus.should == 0 + + ENV['RAILS_ENV'] = 'production' + $: << "#{CONDUCTOR_PATH}/dutils" + require "dutils" + end + + after(:each) do + remove_puppet_manifest + end + + SPEC_MANIFEST = '/tmp/aeolus-spec.pp' + + def create_puppet_manifest(content) + File.open(SPEC_MANIFEST, 'w') { |f| f.write content } + end + + def remove_puppet_manifest + FileUtils.rm SPEC_MANIFEST if File.exist? SPEC_MANIFEST + end + + def run_puppet_manifest + `sudo puppet #{SPEC_MANIFEST} --modulepath=/usr/share/aeolus-configure/modules/` + end + + REQUEST_PREREQS = "$enable_https = true\n"+ + "include aeolus::conductor\n" + + "aeolus::conductor::site_admin{'admin': email => 'dcuser@aeolusproject.org', password => 'password', first_name => 'aeolus', last_name => 'user'}\n" + + "aeolus::conductor::login{'admin': password => 'password', require => Aeolus::Conductor::Site_admin['admin']}\n" + + it "should allow provider creation and deletion" do + create_puppet_manifest(REQUEST_PREREQS + + "aeolus::provider{'mock3010': type => 'mock', port => 3010, require => Aeolus::Conductor::Login['admin'] }\n" + + "aeolus::conductor::logout{'admin': require => Aeolus::Provider['mock3010'] }") + + run_puppet_manifest + $?.exitstatus.should == 0 + Provider.find(:first, :conditions => ['name = ?', 'mock3010']).should_not be_nil, "provider mock3010 should not be nil" + # TODO actually verify service is running + remove_puppet_manifest + + create_puppet_manifest(REQUEST_PREREQS + + "aeolus::deltacloud::disabled{'mock3010': }\n" + + "aeolus::conductor::logout{'admin': require => Aeolus::Deltacloud::Disabled['mock3010'] }") + run_puppet_manifest + $?.exitstatus.should == 0 + #Provider.find(:first, :conditions => ['name = ?', 'mock3010']).should be_nil, "provider mock3010 should be nil" + # TODO actually verify service is down + remove_puppet_manifest + end + + it "should allow new provider account creation" do + create_puppet_manifest(REQUEST_PREREQS + + "aeolus::provider{'mock3020': type => 'mock', port => 3020, require => Aeolus::Conductor::Login['admin'] }\n" + + "aeolus::conductor::provider::account{'mockuser3020': provider => 'mock3020', type => 'mock', username => 'mockuser', password => 'mockpassword', require => Aeolus::Provider['mock3020'] }\n" + + "aeolus::conductor::logout{'admin': require => Aeolus::Conductor::Provider::Account['mockuser3020'] }\n") + run_puppet_manifest + $?.exitstatus.should == 0 + p = Provider.find(:first, :conditions => ['name = ?', 'mock3020']) + pa = p.provider_accounts.first + pa.credentials.size.should == 2 + pa.credentials[0].value.should == "mockuser" + pa.credentials[1].value.should == "mockpassword" + remove_puppet_manifest + + create_puppet_manifest(REQUEST_PREREQS + + "aeolus::deltacloud::disabled{'mock3020':}\n" + + "aeolus::conductor::logout{'admin': require => Aeolus::Deltacloud::Disabled['mock3020'] }\n") + run_puppet_manifest + end + + it "should allow new hwp creation" do + create_puppet_manifest(REQUEST_PREREQS + + "aeolus::conductor::hwp{'hwp123': memory => '512', cpu => '1', storage => '', architecture => 'x86_64', require => Aeolus::Conductor::Login['admin'] }\n" + + "aeolus::conductor::logout{'admin': require => Aeolus::Conductor::Hwp['hwp123'] }\n") + run_puppet_manifest + $?.exitstatus.should == 0 + HardwareProfile.find(:first, :conditions => ['name = ?', 'hwp123']).should_not be_nil, "hwp123 should not be nil" + remove_puppet_manifest + + create_puppet_manifest(REQUEST_PREREQS + + "aeolus::deltacloud::disabled{'mock3020': }\n" + + "aeolus::conductor::logout{'admin': require => Aeolus::Deltacloud::Disabled['mock3020'] }\n") + run_puppet_manifest + end + + it "should allow new image creation" do + create_puppet_manifest(REQUEST_PREREQS + + "aeolus::provider{'mock3030': type => 'mock', port => 3030, require => Aeolus::Conductor::Login['admin'] }\n" + + "aeolus::conductor::provider::account{'mockuser3030': provider => 'mock3030', type => 'mock', username => 'mockuser', password => 'mockpassword', require => Aeolus::Provider['mock3030'] }\n" + + "aeolus::conductor::hwp{'hwp234': memory => '512', cpu => '1', storage => '', architecture => 'x86_64', require => Aeolus::Conductor::Login['admin'] }" + + "aeolus::image{image543: target => 'mock', template => 'examples/custom_repo.tdl', provider => 'mock3030',\n" + + " require => [Aeolus::Conductor::Provider::Account['mockuser3030'], Aeolus::Conductor::Hwp['hwp234']] }\n" + + "aeolus::conductor::logout{'admin': require => Aeolus::Image['image543'] }\n") + run_puppet_manifest + $?.exitstatus.should == 0 + + # verify deployable + File.exist?('/var/www/html/deployables/image543.xml').should be_true + (File.read('/var/www/html/deployables/image543.xml') =~ /.*image id='([a-fA-F0-9-]*)'.*/).should be_true + build = $1 + + # verify image in iwhd + r = Curl::Easy.http_get 'http://localhost:9090/images' + (r.body_str =~ /.*<key>#{build}</key>.*/).should be_true + + # verify deployment and instance in conductor + # TODO verify instance is in 'running' state + Deployment.find(:first, :conditions => ['name = ?', 'image543']).should_not be_nil + Instance.find(:first, :conditions => ['name = ?', 'image543/image543']).should_not be_nil + + remove_puppet_manifest + + create_puppet_manifest(REQUEST_PREREQS + + "aeolus::deltacloud::disabled{'mock3030': }\n" + + "aeolus::conductor::logout{'admin': require => Aeolus::Deltacloud::Disabled['mock3030'] }\n") + run_puppet_manifest + end +end
--- recipes/aeolus/manifests/deltacloud.pp | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/recipes/aeolus/manifests/deltacloud.pp b/recipes/aeolus/manifests/deltacloud.pp index dc367da..26c987b 100644 --- a/recipes/aeolus/manifests/deltacloud.pp +++ b/recipes/aeolus/manifests/deltacloud.pp @@ -36,7 +36,7 @@ define aeolus::deltacloud($provider_type="", $endpoint='', $port="3002") { ensure => 'running', enable => true, require => [Package['deltacloud-core'], - Package['rubygem-aws'], + $provider_type ? { "ec2" => Package['rubygem-aws'], default => Package['deltacloud-core'] }, File["/etc/init.d/deltacloud-${name}", "/var/log/deltacloud-${name}"]] } }
--- contrib/aeolus-configure.spec | 12 ++++++++---- 1 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/contrib/aeolus-configure.spec b/contrib/aeolus-configure.spec index cd03711..3a3d0f7 100644 --- a/contrib/aeolus-configure.spec +++ b/contrib/aeolus-configure.spec @@ -3,8 +3,8 @@
Summary: Aeolus Configure Puppet Recipe Name: aeolus-configure -Version: 2.0.1 -Release: 2%{?dist}%{?extra_release} +Version: 2.0.2 +Release: 1%{?dist}%{?extra_release}
Group: Applications/Internet License: GPLv2+ @@ -22,7 +22,8 @@ Requires: rubygem(uuidtools) # create buckets, eventually replace w/ an # iwhd client Requires: curl -Requires: rubygem-curb +Requires: rubygem(curb) +Requires: rubygem(highline)
%description Aeolus Configure Puppet Recipe @@ -36,7 +37,7 @@ Aeolus Configure Puppet Recipe %{__mkdir} -p %{buildroot}/%{aeolushome}/modules/aeolus %{buildroot}/%{_sbindir} %{__mkdir} -p %{buildroot}%{_sysconfdir}/aeolus-configure/nodes %{__cp} -R %{pbuild}/conf/* %{buildroot}%{_sysconfdir}/aeolus-configure/nodes -%{__cp} -R %{pbuild}/recipes/aeolus/*/ %{buildroot}/%{aeolushome}/modules/aeolus +%{__cp} -R %{pbuild}/recipes/aeolus/* %{buildroot}/%{aeolushome}/modules/aeolus %{__cp} -R %{pbuild}/recipes/apache/ %{buildroot}/%{aeolushome}/modules/apache %{__cp} -R %{pbuild}/recipes/ntp/ %{buildroot}/%{aeolushome}/modules/ntp %{__cp} -R %{pbuild}/recipes/openssl/ %{buildroot}/%{aeolushome}/modules/openssl @@ -53,6 +54,9 @@ Aeolus Configure Puppet Recipe %{aeolushome}
%changelog +* Wed Aug 03 2011 Mo Morsi mmorsi@redhat.com 2.0.2-1 +- update to include profiles, interactive installer + * Wed Jul 20 2011 Mo Morsi mmorsi@redhat.com 2.0.1-2 - updates to conform to Fedora package guidelines
--- .../controllers/provider_accounts_controller.rb | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/src/app/controllers/provider_accounts_controller.rb b/src/app/controllers/provider_accounts_controller.rb index 59616a0..bf5716c 100644 --- a/src/app/controllers/provider_accounts_controller.rb +++ b/src/app/controllers/provider_accounts_controller.rb @@ -55,6 +55,10 @@ class ProviderAccountsController < ApplicationController end
def create + unless params[:provider_account][:provider].nil? + provider = params[:provider_account].delete(:provider) + params[:provider_account][:provider_id] = Provider.find_by_name(provider).id + end @selected_provider = @provider = Provider.find(params[:provider_account][:provider_id]) require_privilege(Privilege::CREATE, ProviderAccount, @provider)
--- src/app/controllers/deployments_controller.rb | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/src/app/controllers/deployments_controller.rb b/src/app/controllers/deployments_controller.rb index ee3039e..5acbf55 100644 --- a/src/app/controllers/deployments_controller.rb +++ b/src/app/controllers/deployments_controller.rb @@ -69,6 +69,7 @@ class DeploymentsController < ApplicationController @deployment = Deployment.new(params[:deployment]) require_privilege(Privilege::CREATE, Deployment, @deployment.pool) @deployment.owner = current_user + @deployment.accessible_and_valid_deployable_xml?(get_deployable_url) respond_to do |format| if @deployment.save status = @deployment.launch(current_user)
--- recipes/aeolus/manifests/profiles/default.pp | 2 +- spec/seed_data_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/recipes/aeolus/manifests/profiles/default.pp b/recipes/aeolus/manifests/profiles/default.pp index 9a470f6..2a9303a 100644 --- a/recipes/aeolus/manifests/profiles/default.pp +++ b/recipes/aeolus/manifests/profiles/default.pp @@ -37,7 +37,7 @@ class aeolus::profiles::default { aeolus::conductor::hwp{"hwp1": memory => "512", cpu => "1", - storage => "", + storage => "1", architecture => "x86_64", require => Aeolus::Conductor::Login["admin"] }
diff --git a/spec/seed_data_spec.rb b/spec/seed_data_spec.rb index 8eb1651..ff3e231 100644 --- a/spec/seed_data_spec.rb +++ b/spec/seed_data_spec.rb @@ -87,7 +87,7 @@ describe "aeolus-configure seed data api" do
it "should allow new hwp creation" do create_puppet_manifest(REQUEST_PREREQS + - "aeolus::conductor::hwp{'hwp123': memory => '512', cpu => '1', storage => '', architecture => 'x86_64', require => Aeolus::Conductor::Login['admin'] }\n" + + "aeolus::conductor::hwp{'hwp123': memory => '512', cpu => '1', storage => '1', architecture => 'x86_64', require => Aeolus::Conductor::Login['admin'] }\n" + "aeolus::conductor::logout{'admin': require => Aeolus::Conductor::Hwp['hwp123'] }\n") run_puppet_manifest $?.exitstatus.should == 0 @@ -104,7 +104,7 @@ describe "aeolus-configure seed data api" do create_puppet_manifest(REQUEST_PREREQS + "aeolus::provider{'mock3030': type => 'mock', port => 3030, require => Aeolus::Conductor::Login['admin'] }\n" + "aeolus::conductor::provider::account{'mockuser3030': provider => 'mock3030', type => 'mock', username => 'mockuser', password => 'mockpassword', require => Aeolus::Provider['mock3030'] }\n" + - "aeolus::conductor::hwp{'hwp234': memory => '512', cpu => '1', storage => '', architecture => 'x86_64', require => Aeolus::Conductor::Login['admin'] }" + + "aeolus::conductor::hwp{'hwp234': memory => '1', cpu => '1', storage => '1', architecture => 'x86_64', require => Aeolus::Conductor::Login['admin'] }" + "aeolus::image{image543: target => 'mock', template => 'examples/custom_repo.tdl', provider => 'mock3030',\n" + " require => [Aeolus::Conductor::Provider::Account['mockuser3030'], Aeolus::Conductor::Hwp['hwp234']] }\n" + "aeolus::conductor::logout{'admin': require => Aeolus::Image['image543'] }\n")
--- conf/aeolus-conductor | 5 +++-- conf/aeolus-conductor.sysconf | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/conf/aeolus-conductor b/conf/aeolus-conductor index b525ec1..ad3b570 100755 --- a/conf/aeolus-conductor +++ b/conf/aeolus-conductor @@ -15,6 +15,7 @@ RAILS_ENV="${RAILS_ENV:-production}" CONDUCTOR_DIR="${CONDUCTOR_DIR:-/usr/share/aeolus-conductor}" CONDUCTOR_USER="${CONDUCTOR_USER:-aeolus}" +CONDUCTOR_GROUP="${CONDUCTOR_GROUP:-aeolus}" THIN_PROG="${THIN_PROG:-thin}" THIN_PID="${THIN_PID:-/var/run/aeolus-conductor/thin.pid}" THIN_LOG="${THIN_LOG:-/var/log/aeolus-conductor/thin.log}" @@ -38,8 +39,8 @@ start() { fi
$THIN_PROG start -c "$CONDUCTOR_DIR" -l "$THIN_LOG" -P "$THIN_PID" \ - -a $ADDR -e $RAILS_ENV --user $CONDUCTOR_USER -d --prefix=$PREFIX \ - --rackup "$CONDUCTOR_DIR/config.ru" + -a $ADDR -e $RAILS_ENV --user $CONDUCTOR_USER -g $CONDUCTOR_GROUP \ + -d --prefix=$PREFIX --rackup "$CONDUCTOR_DIR/config.ru" RETVAL=$? if [ $RETVAL -eq 0 ] ; then # wait until we can contact the server diff --git a/conf/aeolus-conductor.sysconf b/conf/aeolus-conductor.sysconf index 63bc0ee..74d73e4 100644 --- a/conf/aeolus-conductor.sysconf +++ b/conf/aeolus-conductor.sysconf @@ -4,6 +4,9 @@ # user under which Rails application runs #CONDUCTOR_USER=aeolus
+# group under which Rails application runs +#CONDUCTOR_GROUP=aeolus + # location of the thin program #THIN_PROG=thin
On 08/03/2011 09:49 PM, Mo Morsi wrote:
The following patchset implements several new features for aeolus-configure including
configuration profiles or grouping of aeolus components and seed data which can be configured seperately from each other
interactive installer allowing the user to select which components are configured locally and the parameters to seed the application with
expanded seed data interface including support for provider accounts and images
expanded spec suite, verifying all the above
a few bug fixes / improvements to smoothen operations
Everything has been tested, all existing functionality still fully works as does the new additions _______________________________________________ aeolus-devel mailing list aeolus-devel@lists.fedorahosted.org https://fedorahosted.org/mailman/listinfo/aeolus-devel
Ack with follow on patches posted by Mo today.
Note interactive installer is still preliminary. Options 2-5 do not work and will be improved upon.
Ack with follow on patches posted by Mo today.
Note interactive installer is still preliminary. Options 2-5 do not work and will be improved upon. _______________________________________________ aeolus-devel mailing list aeolus-devel@lists.fedorahosted.org https://fedorahosted.org/mailman/listinfo/aeolus-devel
Thank you greatly, rebased and pushed this patchset and the followup.
-Mo
On Wed, 2011-08-10 at 17:01 -0400, Mo Morsi wrote:
Ack with follow on patches posted by Mo today.
Note interactive installer is still preliminary. Options 2-5 do not work and will be improved upon. _______________________________________________ aeolus-devel mailing list aeolus-devel@lists.fedorahosted.org https://fedorahosted.org/mailman/listinfo/aeolus-devel
Thank you greatly, rebased and pushed this patchset and the followup.
-Mo _______________________________________________ aeolus-devel mailing list aeolus-devel@lists.fedorahosted.org https://fedorahosted.org/mailman/listinfo/aeolus-devel
aeolus-devel@lists.fedorahosted.org