diff --git a/.gitignore b/.gitignore index 3e57bca..84f5240 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ *.swp *.swo pkg/* + +## RubyMine +.idea \ No newline at end of file diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..8926307 --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source 'http://rubygems.org' + +gemspec \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..12aab0a --- /dev/null +++ b/LICENSE @@ -0,0 +1,10 @@ +Copyright (c) 2012 RightScale, Inc, All Rights Reserved Worldwide. + +THIS PROGRAM IS CONFIDENTIAL AND PROPRIETARY TO RIGHTSCALE +AND CONSTITUTES A VALUABLE TRADE SECRET. Any unauthorized use, +reproduction, modification, or disclosure of this program is +strictly prohibited. Any use of this program by an authorized +licensee is strictly subject to the terms and conditions, +including confidentiality obligations, set forth in the applicable +License Agreement between RightScale, Inc. and +the licensee. diff --git a/README.md b/README.md new file mode 100644 index 0000000..86e4a99 --- /dev/null +++ b/README.md @@ -0,0 +1,140 @@ +# RightScale REST Connection + +The rest_connection gem is a Ruby library for RightScale's API 0.1, 1.0 and 1.5. + +Legacy clusters: +- API 0.1 AWS clouds +- API 1.0 AWS clouds +- API 1.5 non-AWS clouds + +Unified clusters: +- API 1.0 AWS clouds +- API 1.5 all clouds + +This gem also supports RightScale's instance facing API 1.0, which use the instance token to login. +The instance token is found in the instance's user data as 'RS_rn_auth' or alternatively as part of 'RS_api_url'. +The user data is available under the 'Info' tab on the server's page in the RightScale Dashboard. + +This gem should be considered deprecated! + +If you only use API 1.5, you should use the right_api_client gem instead: +https://rubygems.org/gems/right_api_client + +- API 1.0 Documentation: http://support.rightscale.com/12-Guides/03-RightScale_API +- API 1.0 Reference Docs: http://reference.rightscale.com/api1.0 +- API 1.5 Documentation: http://support.rightscale.com/12-Guides/RightScale_API_1.5 +- API 1.5 Reference Docs: http://reference.rightscale.com/api1.5 + +Maintained by the RightScale "Yellow_team" + +## Installation + +Ruby 1.8.7 or higher is required. + +### Installing from RubyGems + + "gem install rest_connection" + +### Installing from source + + "git clone git@github.com:rightscale/rest_connection.git" + "cd rest_connection" + "gem install rconf" + "rconf" <- follow any further instructions from rconf + "bundle install" + +## Versioning + +This gem follows semantic versioning: http://semver.org + +## Usage Instructions + +You must setup '~/.rest_connection/rest_api_config.yaml' or '/etc/rest_connection/rest_api_config.yaml' + +Copy the example from '$GEMHOME/rest_connection/config/rest_api_config.yaml.sample' and fill in your connection info. + +Pro Tip: to find a $GEMHOME, use gemedit + + "gem install gemedit" + "gem edit rest_connection" + +The following examples assume an interactive ruby session (irb): + + $ bundle exec irb + ruby> require 'rubygems'; require 'rest_connection' + +### Look up and run a RightScript + + first_fe = Server.find(:first) { |s| s.nickname =~ /Front End/ } + st = ServerTemplate.find(first_fe.server_template_href) + connect_script = st.executables.detect { |ex| ex.name =~ /LB [app|mongrels]+ to HA proxy connect/i } + state = first_fe.run_executable(connect_script) + state.wait_for_completed + +### Stop a Deployment + + deployment = Deployment.find(opts[:id]) + my_servers = deployment.servers + my_servers.each { |s| s.stop } + my_servers.each { |s| s.wait_for_state("stopped") } + +### Activate an Ec2ServerArray / Display instances IPs + + my_array = Ec2ServerArray.find(opts[:href]) + my_array.active = true + my_array.save + + puts my_array.instances.map { |i| i['ip-address'] } + +## Design Decisions + +Currently, all API resources are represented by classes in 'lib/rest_connection/rightscale'. +Various helper modules are also located in this directory. + +### API 0.1 resources + +API 0.1 resources are often named with suffix '_internal'. + +They often pull in common internal code: + + include RightScale::Api::Internal + extend RightScale::Api::InternalExtend + +### API 1.0 resources + +API 1.0 resources are often named with prefix 'ec2_' for Amazon Elastic Compute Cloud. + +They often pull in common internal code: + + include RightScale::Api::Base + extend RightScale::Api::BaseExtend + +### API 1.5 resources + +API 1.5 resources are often named with prefix 'mc_' for MultiCloud. +They often talk to the MultiCloud gateway and therefore pull in some common gateway code: + + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + +## Troubleshooting + +### Wrong ruby version + +Ruby 1.8.7 or higher is required. + +## Publishing + +To cut a new gem and push to RubyGems: + +Edit lib/rest_connection/version.rb with semantic version number. + + "bundle exec gem build rest_connection.gemspec" + "ls *.gem" <- verify that gem was built + "cd /tmp" + "bundle exec gem install /path/to/local/rest_connection-X.Y.Z.gem" <- replace X.Y.Z with your new version number + "bundle exec gem uninstall rest_connection" + "cd -" + "bundle exec gem push rest_connection-X.Y.Z.gem" + +Check it out: https://rubygems.org/gems/rest_connection diff --git a/README.rdoc b/README.rdoc deleted file mode 100644 index c9dfdbf..0000000 --- a/README.rdoc +++ /dev/null @@ -1,49 +0,0 @@ -= rest_connection Quick Start -== Install -==== Installing with rubygems - "gem install rest_connection" - -==== Installing from source - "git clone http://github.com/jeremyd/rest_connection.git" - "gem install jeweler rspec" - "rake check_dependencies" <- Install any gems listed. - "rake install" - -== Configuration - -You must setup ~/.rest_connection/rest_api_config.yaml or /etc/rest_connection/rest_api_config.yaml - -Copy the example from GEMHOME/rest_connection/examples/rest_api_config.yaml.sample and fill in your connection info. - - Pro Tip: to find a GEMHOME, use gemedit - "gem install gemedit" - "gem edit rest_connection" - -== Usage: some IRB samples - - $ irb - ruby> require 'rubygems'; require 'rest_connection' - -=== Lookup and run a RightScript - - first_fe = Server.find(:first) { |s| s.nickname =~ /Front End/ } - st = ServerTemplate.find(first_fe.server_template_href) - connect_script = st.executables.detect { |ex| ex.name =~ /LB [app|mongrels]+ to HA proxy connect/i } - state = first_fe.run_executable(connect_script) - state.wait_for_completed - -=== Stop a Deployment - - deployment = Deployment.find(opts[:id]) - my_servers = deployment.servers - my_servers.each { |s| s.stop } - my_servers.each { |s| s.wait_for_state("stopped") } - -=== Activate an Ec2ServerArray / Display instances IPs - - my_array = Ec2ServerArray.find(opts[:href]) - my_array.active = true - my_array.save - - puts my_array.instances.map { |i| i['ip-address'] } - diff --git a/Rakefile b/Rakefile index 3abb5a3..2caad53 100644 --- a/Rakefile +++ b/Rakefile @@ -1,15 +1,17 @@ -require 'rubygems' -require 'jeweler' -Jeweler::Tasks.new do |gemspec| - gemspec.name = "rest_connection" - gemspec.summary = "lib for restful connections to the rightscale api" - gemspec.description = "provides rest_connection" - gemspec.email = "jeremy@rubyonlinux.org" - gemspec.homepage = "http://github.com/jeremyd/rest_connection" - gemspec.authors = ["Jeremy Deininger"] - gemspec.add_dependency('activesupport', "=2.3.10") - gemspec.add_dependency('net-ssh') - gemspec.add_dependency('json') +require File.expand_path('../lib/rest_connection', __FILE__) +require 'rake' +require 'spec/rake/spectask' + +task :build do + system "gem build rest_connection.gemspec" +end + +task :release => :build do + system "gem push rest_connection-#{RestConnection::VERSION}.gem" end -Jeweler::GemcutterTasks.new +Spec::Rake::SpecTask.new('spec') do |t| + t.spec_files = Dir.glob('spec/*_spec.rb') + t.spec_opts << '--format nested' + t.spec_opts << '--colour' +end diff --git a/VERSION b/VERSION deleted file mode 100644 index fe059cc..0000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.0.22 \ No newline at end of file diff --git a/config/rest_api_config.yaml.sample b/config/rest_api_config.yaml.sample index 30f72eb..79d17cf 100644 --- a/config/rest_api_config.yaml.sample +++ b/config/rest_api_config.yaml.sample @@ -1,10 +1,14 @@ ---- -:ssh_keys: +--- +:ssh_keys: - ~/.ssh/my_server_key - ~/.ssh/my_server_key-eu - ~/.ssh/my_server_key-west :pass: myUltraSecurePasswordz :user: myUserEmail@MyMailDomain.com :api_url: https://my.rightscale.com/api/acct/00000000 -:common_headers: +:common_headers: X_API_VERSION: "1.0" +:azure_hack_on: true +:azure_hack_retry_count: 5 +:azure_hack_sleep_seconds: 60 +:api_logging: false diff --git a/examples/console.rb b/examples/console.rb index 4d8d9df..2a38cad 100644 --- a/examples/console.rb +++ b/examples/console.rb @@ -1,3 +1,26 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + require 'rubygems' require 'rest_connection' require 'ruby-debug' diff --git a/examples/cucumber/step_definitions/deployment_steps.rb b/examples/cucumber/step_definitions/deployment_steps.rb index 069885f..79cee2b 100644 --- a/examples/cucumber/step_definitions/deployment_steps.rb +++ b/examples/cucumber/step_definitions/deployment_steps.rb @@ -1,3 +1,26 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + require "rubygems" require "rest_connection" require "net/ssh" diff --git a/examples/cucumber/step_definitions/mysql_steps.rb b/examples/cucumber/step_definitions/mysql_steps.rb index 0ad398c..4867e20 100644 --- a/examples/cucumber/step_definitions/mysql_steps.rb +++ b/examples/cucumber/step_definitions/mysql_steps.rb @@ -1,3 +1,26 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + require 'rubygems' require 'sqlite3' diff --git a/examples/cucumber/step_definitions/recipe_steps.rb b/examples/cucumber/step_definitions/recipe_steps.rb index 9b43800..e08ac80 100644 --- a/examples/cucumber/step_definitions/recipe_steps.rb +++ b/examples/cucumber/step_definitions/recipe_steps.rb @@ -1,3 +1,26 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + When /^I run a recipe named "([^\"]*)" on server "([^\"]*)"\.$/ do |recipe, server_index| human_index = server_index.to_i - 1 STDOUT.puts "#{recipe} -> root@#{@servers[human_index].dns_name}" diff --git a/examples/cucumber/step_definitions/spot_check_steps.rb b/examples/cucumber/step_definitions/spot_check_steps.rb index 393482b..fda0130 100644 --- a/examples/cucumber/step_definitions/spot_check_steps.rb +++ b/examples/cucumber/step_definitions/spot_check_steps.rb @@ -1,3 +1,26 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + Then /^I should run a command "([^\"]*)" on server "([^\"]*)"\.$/ do |command, server_index| human_index = server_index.to_i - 1 @servers[human_index].spot_check(command) { |result| puts result } diff --git a/examples/relaunch_deployment.rb b/examples/relaunch_deployment.rb index 3e9f025..34504e2 100644 --- a/examples/relaunch_deployment.rb +++ b/examples/relaunch_deployment.rb @@ -1,4 +1,28 @@ #!/usr/bin/env ruby + +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + require 'rubygems' require 'trollop' require 'rest_connection' diff --git a/examples/right_scale_ec2_instances_api_test.rb b/examples/right_scale_ec2_instances_api_test.rb index b0c6354..0e8c55e 100644 --- a/examples/right_scale_ec2_instances_api_test.rb +++ b/examples/right_scale_ec2_instances_api_test.rb @@ -1,3 +1,26 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + require 'rubygems' require 'rest_connection' require 'spec' diff --git a/git_hooks/pre-commit b/git_hooks/pre-commit new file mode 100755 index 0000000..c3e45a1 --- /dev/null +++ b/git_hooks/pre-commit @@ -0,0 +1,91 @@ +#!/bin/bash + +# Setup $stat_flag based on OS type +mac_unix_name=Darwin +uname_returned=`uname -a` +unix_name=${uname_returned:0:${#mac_unix_name}} +if [ "$unix_name" = "$mac_unix_name" ]; then + echo "MAC OS \"$mac_unix_name\" detected..." + stat_flag=-f +else + echo "non-MAC OS detected..." + stat_flag=-c +fi + +echo "Checking for syntax errors..." +for FILE in `git diff-index --name-only HEAD --` ; do + if test -f $FILE; then + # If a file is ruby, check for syntax errors using ruby + if [[ "$FILE" =~ .*\.rb$ ]]; then + if [[ "$fail" -eq 0 || -z "$fail" ]]; then + ruby -c "$FILE" 1> /dev/null; fail=$? + if [[ "$fail" -ne 0 ]]; then echo "Syntax Error found in '$FILE'"; fi + else + ruby -c "$FILE" 1> /dev/null + if [[ "$?" -ne 0 ]]; then echo "Syntax Error found in '$FILE'"; fi + fi + fi + + # If a file is json, check for syntax errors + if [[ "$FILE" =~ .*\.json$ ]]; then + if [[ "$fail" -eq 0 || -z "$fail" ]]; then + ruby -e "require 'rubygems'; require 'json'; JSON::parse(IO.read('$FILE'))" 1> /dev/null; fail=$? + if [[ "$fail" -ne 0 ]]; then echo "Syntax Error found in '$FILE'"; fi + else + ruby -e "require 'rubygems'; require 'json'; JSON::parse(IO.read('$FILE'))" 1> /dev/null + if [[ "$?" -ne 0 ]]; then echo "Syntax Error found in '$FILE'"; fi + fi + fi + + # If a file is yaml, check for syntax errors + if [[ "$FILE" =~ .*\.yaml$ ]]; then + if [[ "$fail" -eq 0 || -z "$fail" ]]; then + ruby -e "require 'rubygems'; require 'yaml'; YAML::load(IO.read('$FILE'))" 1> /dev/null; fail=$? + if [[ "$fail" -ne 0 ]]; then echo "Syntax Error found in '$FILE'"; fi + else + ruby -e "require 'rubygems'; require 'yaml'; YAML::load(IO.read('$FILE'))" 1> /dev/null + if [[ "$?" -ne 0 ]]; then echo "Syntax Error found in '$FILE'"; fi + fi + fi + fi +done +echo "Syntax check complete." + +# Built-in git checks +git diff-index --check HEAD -- + +if [[ "$fail" -ne 0 && -n "$fail" ]]; then + echo "Syntax Errors Found. Aborting commit" + exit 1 +fi + +# Check for warnings +fail=0 +for FILE in `git diff-index --name-only HEAD --` ; do + if test -e $FILE; then + # If a file is ruby, check for syntax errors + if [[ -n `find $FILE -regex ".*\.rb$"` ]]; then + warnings=`ruby -c "$FILE" 2>&1 | grep -i warn` + if [[ -n "$warnings" ]]; then fail=1; fi + fi + fi +done + +if [[ "$fail" -ne 0 && -n "$fail" ]]; then + echo "Syntax Warnings Found. Aborting commit" + exit 1 +fi + +# Check that username and user emails are filled properly +username=`git config --get user.name` +useremail=`git config --get user.email` +emaildomain=`echo $useremail | grep -o "[^@]*$"` +if [[ "$username" == "Put Your Name Here" || "$username" == "" ]]; then + echo "Please set your git user.name by running 'git config user.name '" + exit 1 +elif [[ "$useremail" == "setyouremail@rightscale.com" || "$useremail" == "" ]] || ! host "$emaildomain" &> /dev/null; then + echo "Please set your git user.email by running 'git config user.email '" + exit 1 +fi + +exit 0 diff --git a/lib/rest_connection.rb b/lib/rest_connection.rb index f7083ff..8dbf58c 100644 --- a/lib/rest_connection.rb +++ b/lib/rest_connection.rb @@ -1,17 +1,25 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ require 'net/https' require 'rubygems' @@ -19,7 +27,14 @@ require 'yaml' require 'cgi' require 'rest_connection/rightscale/rightscale_api_resources' +require 'rest_connection/patches' require 'logger' +require 'highline/import' +require 'nokogiri' + +# +# REST connection config and error definitions. +# module RestConnection class Connection @@ -35,51 +50,106 @@ class Connection # Settings are loaded from a yaml configuration file in users home directory. # Copy the example config from the gemhome/config/rest_api_config.yaml.sample to ~/.rest_connection/rest_api_config.yaml # OR to /etc/rest_connection/rest_api_config.yaml + # Here's an example of overriding the settings in the configuration file: + # Server.connection.settings[:api_url] = "https://my.rightscale.com/api/acct/1234" # def initialize(config_yaml = File.join(File.expand_path("~"), ".rest_connection", "rest_api_config.yaml")) @@logger = nil + @@user = nil + @@pass = nil etc_config = File.join("#{File::SEPARATOR}etc", "rest_connection", "rest_api_config.yaml") - if File.exists?(config_yaml) + app_bin_dir = File.expand_path(File.dirname(caller.last)) + app_yaml = File.join(app_bin_dir,"..","config","rest_api_config.yaml") + if config_yaml.is_a?(Hash) + @settings = config_yaml + elsif File.exists?(app_yaml) + @settings = YAML::load(IO.read(app_yaml)) + elsif File.exists?(config_yaml) @settings = YAML::load(IO.read(config_yaml)) elsif File.exists?(etc_config) @settings = YAML::load(IO.read(etc_config)) else - logger("\nWARNING: you must setup config file rest_api_config.yaml in #{config_yaml} or #{etc_config}") + logger("\nWARNING: you must setup config file rest_api_config.yaml in #{app_yaml} or #{config_yaml} or #{etc_config}") logger("WARNING: see GEM_HOME/rest_connection/config/rest_api_config.yaml for example config") @settings = {} end + @settings.keys.each { |k| @settings[k.to_sym] = @settings[k] if String === k } + @settings[:extension] = ".js" @settings[:api_href] = @settings[:api_url] unless @settings[:api_href] + unless @settings[:user] + @@user = ask("Username:") unless @@user + @settings[:user] = @@user + end + unless @settings[:pass] + @@pass = ask("Password:") { |q| q.echo = false } unless @@pass + @settings[:pass] = @@pass + end + @settings[:azure_hack_on] ||= true + @settings[:azure_hack_retry_count] ||= 5 + @settings[:azure_hack_sleep_seconds] ||= 60 + @settings[:api_logging] ||= false end # Main HTTP connection loop. Common settings are set here, then we yield(BASE_URI, OPTIONAL_HEADERS) to other methods for each type of HTTP request: GET, PUT, POST, DELETE - # + # # The block must return a Net::HTTP Request. You have a chance to taylor the request inside the block that you pass by modifying the url and headers. # # rest_connect do |base_uri, headers| # headers.merge! {:my_header => "blah"} # Net::HTTP::Get.new(base_uri, headers) # end - # + # def rest_connect(&block) uri = URI.parse(@settings[:api_href]) http = Net::HTTP.new(uri.host, uri.port) if uri.scheme == 'https' - http.use_ssl = true + http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE end headers = @settings[:common_headers] - headers.merge!("Cookie" => @cookie) if @cookie http.start do |http| - req = yield(uri, headers) - unless @cookie - req.basic_auth(@settings[:user], @settings[:pass]) if @settings[:user] + @max_retries = 3 + ret = nil + begin + headers.delete("Cookie") + headers.merge!("Cookie" => @cookie) if @cookie + req = yield(uri, headers) + logger("#{req.method}: #{req.path}") + logger("\trequest body: #{req.body}") if req.body and req.body !~ /password/ + req.basic_auth(@settings[:user], @settings[:pass]) if @settings[:user] unless @cookie + + response, body = http.request(req) + ret = handle_response(response) + rescue Exception => e + raise unless error_handler(e) + retry + end + ret + end + end + + def error_handler(e) + case e + when EOFError, Timeout::Error + if @max_retries >= 0 + logger("Caught #{e}. Retrying...") + @max_retries -= 1 + return true + end + when RestConnection::Errors::Forbidden + if @max_retries >= 0 + if e.response.body =~ /(session|cookie).*(invalid|expired)/i + logger("Caught '#{e.response.body}'. Refreshing cookie...") + refresh_cookie if respond_to?(:refresh_cookie) + else + return false + end + @max_retries -= 1 + return true end - logger("#{req.method}: #{req.path}") - logger("\trequest body: #{req.body}") if req.body - response, body = http.request(req) - handle_response(response) end + return false end # connection.get("/root/login", :test_header => "x", :test_header2 => "y") @@ -88,13 +158,14 @@ def rest_connect(&block) # additional_parameters = Hash or String of parameters to pass to HTTP::Get def get(href, additional_parameters = "") rest_connect do |base_uri,headers| - href = "#{base_uri}/#{href}" unless begins_with_slash(href) + new_href = (href =~ /^\// ? href : "#{base_uri}/#{href}") + puts("DEBUG: new_href get : #{new_href.inspect}") if @settings[:api_logging] params = requestify(additional_parameters) || "" - new_path = URI.escape(href + @settings[:extension] + "?") + params + new_path = URI.escape(new_href + @settings[:extension] + "?") + params Net::HTTP::Get.new(new_path, headers) end end - + # connection.post(server_url + "/start") # # href = "/api/base_new" if this begins with a slash then the url will be used as absolute path. @@ -102,8 +173,9 @@ def get(href, additional_parameters = "") # additional_parameters = Hash or String of parameters to pass to HTTP::Post def post(href, additional_parameters = {}) rest_connect do |base_uri, headers| - href = "#{base_uri}/#{href}" unless begins_with_slash(href) - res = Net::HTTP::Post.new(href , headers) + new_href = (href =~ /^\// ? href : "#{base_uri}/#{href}") + puts("DEBUG: new_href post : #{new_href.inspect}") if @settings[:api_logging] + res = Net::HTTP::Post.new(new_href , headers) unless additional_parameters.empty? res.set_content_type('application/json') res.body = additional_parameters.to_json @@ -120,9 +192,11 @@ def post(href, additional_parameters = {}) # additional_parameters = Hash or String of parameters to pass to HTTP::Put def put(href, additional_parameters = {}) rest_connect do |base_uri, headers| - href = "#{base_uri}/#{href}" unless begins_with_slash(href) - new_path = URI.escape(href) - req = Net::HTTP::Put.new(new_path, headers) + new_href = (href =~ /^\// ? href : "#{base_uri}/#{href}") + new_path = URI.escape(new_href) + puts("DEBUG: new_href put : #{new_href.inspect}") if @settings[:api_logging] + req = Net::HTTP::Put.new(new_path, headers) + puts("DEBUG: req put : #{req.inspect}") if @settings[:api_logging] req.set_content_type('application/json') req.body = additional_parameters.to_json req @@ -136,8 +210,8 @@ def put(href, additional_parameters = {}) # additional_parameters = Hash or String of parameters to pass to HTTP::Delete def delete(href, additional_parameters = {}) rest_connect do |base_uri, headers| - href = "#{base_uri}/#{href}" unless begins_with_slash(href) - new_path = URI.escape(href) + new_href = (href =~ /^\// ? href : "#{base_uri}/#{href}") + new_path = URI.escape(new_href) req = Net::HTTP::Delete.new(href, headers) req.set_content_type('application/json') req.body = additional_parameters.to_json @@ -146,13 +220,26 @@ def delete(href, additional_parameters = {}) end # handle_response - # res = HTTP response + # res = HTTP response # # decoding and post processing goes here. This is where you may need some customization if you want to handle the response differently (or not at all!). Luckily it's easy to modify based on this handler. def handle_response(res) - if res.code.to_i == 201 - return res['Location'] - elsif [200,203,204,302].detect { |d| d == res.code.to_i } + if res.code.to_i == 201 or res.code.to_i == 202 + # + # In most cases, it's safe to return the location + # + if res['Location'] + return res['Location'] + else + # + # Ec2ServerArrayInternal.run_script_on_instances returns XML. + # We need to parse it to retrieve the href's to audit entries. + # + xml_response = Nokogiri::XML(res.body) + return xml_response.xpath('audit-entries/audit-entry/href').map { |href| href.content } + end + elsif [200,203,204].detect { |d| d == res.code.to_i } + if res.body begin return JSON.load(res.body) @@ -162,15 +249,13 @@ def handle_response(res) else return res end - else - raise "invalid response HTTP code: #{res.code.to_i}, #{res.code}, #{res.body}" + else + raise RestConnection::Errors.status_error(res) end end - def begins_with_slash(href) - href =~ /^\// - end - + # Logs a message at info level to stdout or log file + # FIXME: don't lazy initialize def logger(message) init_message = "Initializing Logging using " if @@logger.nil? @@ -184,7 +269,11 @@ def logger(message) @@logger.info(init_message) end - @@logger.info(message) + if @settings.nil? + @@logger.info(message) + else + @@logger.info("[API v#{@settings[:common_headers]['X_API_VERSION']}] " + message) + end end # used by requestify to build parameters strings @@ -205,6 +294,107 @@ def requestify(parameters, prefix=nil) "#{prefix}=#{CGI.escape(parameters.to_s)}" end end + end + + module Errors + # HTTPStatusErrors, borrowed lovingly from the excon gem <3 + class HTTPStatusError < StandardError + attr_reader :request, :response + def initialize(msg, response = nil, request = nil) + super(msg) + @request = request + @response = response + end + end + + class Continue < HTTPStatusError; end # 100 + class SwitchingProtocols < HTTPStatusError; end # 101 + class OK < HTTPStatusError; end # 200 + class Created < HTTPStatusError; end # 201 + class Accepted < HTTPStatusError; end # 202 + class NonAuthoritativeInformation < HTTPStatusError; end # 203 + class NoContent < HTTPStatusError; end # 204 + class ResetContent < HTTPStatusError; end # 205 + class PartialContent < HTTPStatusError; end # 206 + class MultipleChoices < HTTPStatusError; end # 300 + class MovedPermanently < HTTPStatusError; end # 301 + class Found < HTTPStatusError; end # 302 + class SeeOther < HTTPStatusError; end # 303 + class NotModified < HTTPStatusError; end # 304 + class UseProxy < HTTPStatusError; end # 305 + class TemporaryRedirect < HTTPStatusError; end # 307 + class BadRequest < HTTPStatusError; end # 400 + class Unauthorized < HTTPStatusError; end # 401 + class PaymentRequired < HTTPStatusError; end # 402 + class Forbidden < HTTPStatusError; end # 403 + class NotFound < HTTPStatusError; end # 404 + class MethodNotAllowed < HTTPStatusError; end # 405 + class NotAcceptable < HTTPStatusError; end # 406 + class ProxyAuthenticationRequired < HTTPStatusError; end # 407 + class RequestTimeout < HTTPStatusError; end # 408 + class Conflict < HTTPStatusError; end # 409 + class Gone < HTTPStatusError; end # 410 + class LengthRequired < HTTPStatusError; end # 411 + class PreconditionFailed < HTTPStatusError; end # 412 + class RequestEntityTooLarge < HTTPStatusError; end # 413 + class RequestURITooLong < HTTPStatusError; end # 414 + class UnsupportedMediaType < HTTPStatusError; end # 415 + class RequestedRangeNotSatisfiable < HTTPStatusError; end # 416 + class ExpectationFailed < HTTPStatusError; end # 417 + class UnprocessableEntity < HTTPStatusError; end # 422 + class InternalServerError < HTTPStatusError; end # 500 + class NotImplemented < HTTPStatusError; end # 501 + class BadGateway < HTTPStatusError; end # 502 + class ServiceUnavailable < HTTPStatusError; end # 503 + class GatewayTimeout < HTTPStatusError; end # 504 + + # Messages for nicer exceptions, from rfc2616 + def self.status_error(response) + @errors ||= { + 100 => [RestConnection::Errors::Continue, 'Continue'], + 101 => [RestConnection::Errors::SwitchingProtocols, 'Switching Protocols'], + 200 => [RestConnection::Errors::OK, 'OK'], + 201 => [RestConnection::Errors::Created, 'Created'], + 202 => [RestConnection::Errors::Accepted, 'Accepted'], + 203 => [RestConnection::Errors::NonAuthoritativeInformation, 'Non-Authoritative Information'], + 204 => [RestConnection::Errors::NoContent, 'No Content'], + 205 => [RestConnection::Errors::ResetContent, 'Reset Content'], + 206 => [RestConnection::Errors::PartialContent, 'Partial Content'], + 300 => [RestConnection::Errors::MultipleChoices, 'Multiple Choices'], + 301 => [RestConnection::Errors::MovedPermanently, 'Moved Permanently'], + 302 => [RestConnection::Errors::Found, 'Found'], + 303 => [RestConnection::Errors::SeeOther, 'See Other'], + 304 => [RestConnection::Errors::NotModified, 'Not Modified'], + 305 => [RestConnection::Errors::UseProxy, 'Use Proxy'], + 307 => [RestConnection::Errors::TemporaryRedirect, 'Temporary Redirect'], + 400 => [RestConnection::Errors::BadRequest, 'Bad Request'], + 401 => [RestConnection::Errors::Unauthorized, 'Unauthorized'], + 402 => [RestConnection::Errors::PaymentRequired, 'Payment Required'], + 403 => [RestConnection::Errors::Forbidden, 'Forbidden'], + 404 => [RestConnection::Errors::NotFound, 'Not Found'], + 405 => [RestConnection::Errors::MethodNotAllowed, 'Method Not Allowed'], + 406 => [RestConnection::Errors::NotAcceptable, 'Not Acceptable'], + 407 => [RestConnection::Errors::ProxyAuthenticationRequired, 'Proxy Authentication Required'], + 408 => [RestConnection::Errors::RequestTimeout, 'Request Timeout'], + 409 => [RestConnection::Errors::Conflict, 'Conflict'], + 410 => [RestConnection::Errors::Gone, 'Gone'], + 411 => [RestConnection::Errors::LengthRequired, 'Length Required'], + 412 => [RestConnection::Errors::PreconditionFailed, 'Precondition Failed'], + 413 => [RestConnection::Errors::RequestEntityTooLarge, 'Request Entity Too Large'], + 414 => [RestConnection::Errors::RequestURITooLong, 'Request-URI Too Long'], + 415 => [RestConnection::Errors::UnsupportedMediaType, 'Unsupported Media Type'], + 416 => [RestConnection::Errors::RequestedRangeNotSatisfiable, 'Request Range Not Satisfiable'], + 417 => [RestConnection::Errors::ExpectationFailed, 'Expectation Failed'], + 422 => [RestConnection::Errors::UnprocessableEntity, 'Unprocessable Entity'], + 500 => [RestConnection::Errors::InternalServerError, 'InternalServerError'], + 501 => [RestConnection::Errors::NotImplemented, 'Not Implemented'], + 502 => [RestConnection::Errors::BadGateway, 'Bad Gateway'], + 503 => [RestConnection::Errors::ServiceUnavailable, 'Service Unavailable'], + 504 => [RestConnection::Errors::GatewayTimeout, 'Gateway Timeout'] + } + error, message = @errors[response.code.to_i] || [RestConnection::Errors::HTTPStatusError, 'Unknown'] + error.new("Invalid response HTTP code: #{response.code.to_i}: #{response.body}", response) + end end end diff --git a/lib/rest_connection/patches.rb b/lib/rest_connection/patches.rb new file mode 100644 index 0000000..d866650 --- /dev/null +++ b/lib/rest_connection/patches.rb @@ -0,0 +1,141 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# Hash Patches + +class Hash + # Merges self with another second, recursively. + # + # This code was lovingly stolen from some random gem: + # http://gemjack.com/gems/tartan-0.1.1/classes/Hash.html + # + # Thanks to whoever made it. + # + # Modified to provide same functionality with Arrays + + def deep_merge(second) + target = dup + return target unless second + unless Hash === second + raise TypeError.new("can't convert #{second.class} into #{self.class}") + end + second.keys.each do |k| + if second[k].is_a? Array and self[k].is_a? Array + target[k] = target[k].deep_merge(second[k]) + next + elsif second[k].is_a? Hash and self[k].is_a? Hash + target[k] = target[k].deep_merge(second[k]) + next + end + target[k] = second[k] + end + target + end + + # From: http://www.gemtacular.com/gemdocs/cerberus-0.2.2/doc/classes/Hash.html + # File lib/cerberus/utils.rb, line 42 + # Modified to provide same functionality with Arrays + + def deep_merge!(second) + return nil unless second + unless Hash === second + raise TypeError.new("can't convert #{second.class} into #{self.class}") + end + second.each_pair do |k,v| + if self[k].is_a?(Array) and second[k].is_a?(Array) + self[k].deep_merge!(second[k]) + elsif self[k].is_a?(Hash) and second[k].is_a?(Hash) + self[k].deep_merge!(second[k]) + else + self[k] = second[k] + end + end + end +end + +# Array Patches + +class Array + def deep_merge(second) + target = dup + return target unless second + unless Array === second + raise TypeError.new("can't convert #{second.class} into #{self.class}") + end + second.each_index do |k| + if second[k].is_a? Array and self[k].is_a? Array + target[k] = target[k].deep_merge(second[k]) + next + elsif second[k].is_a? Hash and self[k].is_a? Hash + target[k] = target[k].deep_merge(second[k]) + next + end + target << second[k] unless target.include?(second[k]) + end + target + end + + def deep_merge!(second) + return nil unless second + unless Array === second + raise TypeError.new("can't convert #{second.class} into #{self.class}") + end + second.each_index do |k| + if self[k].is_a?(Array) and second[k].is_a?(Array) + self[k].deep_merge!(second[k]) + elsif self[k].is_a?(Hash) and second[k].is_a?(Hash) + self[k].deep_merge!(second[k]) + else + self << second[k] unless self.include?(second[k]) + end + end + end + + def *(second) + if second.is_a?(Integer) + ret = [] + second.times { |i| ret += dup } + return ret + elsif second.is_a?(Array) + ret = [] + each { |x| second.each { |y| ret << [x,y].flatten } } + return ret + else + raise TypeError.new("can't convert #{second.class} into Integer") + end + end + + def **(second) + if second.is_a?(Integer) + ret = dup + (second - 1).times { + temp = [] + ret.each { |x| each { |y| temp << [x,y].flatten } } + ret = temp + } + return ret + else + raise TypeError.new("can't convert #{second.class} into Integer") + end + end +end diff --git a/lib/rest_connection/rightscale/account.rb b/lib/rest_connection/rightscale/account.rb new file mode 100644 index 0000000..97984f7 --- /dev/null +++ b/lib/rest_connection/rightscale/account.rb @@ -0,0 +1,37 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# + +# +# Account Resource requires "admin" role +# + +class Account + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + + deny_methods :index, :create, :destroy, :update +end diff --git a/lib/rest_connection/rightscale/alert_spec.rb b/lib/rest_connection/rightscale/alert_spec.rb index 7bf7a44..84479c5 100644 --- a/lib/rest_connection/rightscale/alert_spec.rb +++ b/lib/rest_connection/rightscale/alert_spec.rb @@ -1,27 +1,34 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.0 # class AlertSpec include RightScale::Api::Base extend RightScale::Api::BaseExtend - - def attach(params) - connection.post('alert_spec_subjects.js' , :alert_spec_subject => params) + AlertSpecSubject.create(params) end - - -end +end diff --git a/lib/rest_connection/rightscale/alert_spec_subject.rb b/lib/rest_connection/rightscale/alert_spec_subject.rb new file mode 100644 index 0000000..9b3f92b --- /dev/null +++ b/lib/rest_connection/rightscale/alert_spec_subject.rb @@ -0,0 +1,32 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.0 +# +class AlertSpecSubject + include RightScale::Api::Base + extend RightScale::Api::BaseExtend + + deny_methods :index, :destroy, :update, :show +end diff --git a/lib/rest_connection/rightscale/audit_entry.rb b/lib/rest_connection/rightscale/audit_entry.rb index b897740..b99a840 100644 --- a/lib/rest_connection/rightscale/audit_entry.rb +++ b/lib/rest_connection/rightscale/audit_entry.rb @@ -1,18 +1,27 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + + #class AuditEntry # attr_accessor :status, :output # def initialize(opts) @@ -24,25 +33,30 @@ # end #end +# +# API 1.0 +# class AuditEntry include RightScale::Api::Base extend RightScale::Api::BaseExtend + deny_methods :index, :create, :destroy, :update + def wait_for_state(state, timeout=900) while(timeout > 0) reload + return true if state == self.state connection.logger("state is #{self.state}, waiting for #{state}") friendly_url = "https://my.rightscale.com/audit_entries/" friendly_url += self.href.split(/\//).last raise "FATAL error, #{self.summary}\nSee Audit: API:#{self.href}, WWW:#{friendly_url}\n" if self.state == 'failed' sleep 30 timeout -= 30 - return true if state == self.state end raise "FATAL: Timeout waiting for Executable to complete. State was #{self.state}" if timeout <= 0 end - def wait_for_completed(legacy=nil) - wait_for_state("completed") + def wait_for_completed(timeout=900) + wait_for_state("completed", timeout) end -end +end diff --git a/lib/rest_connection/rightscale/backup.rb b/lib/rest_connection/rightscale/backup.rb new file mode 100644 index 0000000..5180e68 --- /dev/null +++ b/lib/rest_connection/rightscale/backup.rb @@ -0,0 +1,78 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +class Backup + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + + deny_methods :index + + def self.filters + [:cloud_href, :committed, :completed, :from_master, :latest_before] + end + + def self.find_all(lineage) + index(lineage) + end + + def self.find_with_filter(lineage, filter={}) + index(lineage, filter) + end + + def self.index(lineage, filter={}) + filter_params = [] + filter.each { |key,val| + unless self.filters.include?(key.to_sym) + raise ArgumentError.new("#{key} is not a valid filter for resource #{self.resource_singular_name}") + end + filter_params << "#{key}==#{val}" + } + + a = Array.new + url = self.resource_plural_name + hsh = {'lineage' => lineage} + hsh.merge(:filter => filter_params) unless filter_params.empty? + connection.get(url, hsh).each do |object| + a << self.new(object) + end + + return a + end + + def self.cleanup(lineage, keep_last, params={}) + params.merge!('keep_last' => keep_last, 'lineage' => lineage) + connection.post(resource_plural_name + "/cleanup", params) + end + + def restore(instance_href, name=nil, description=nil) + uri = URI.parse(self.href) + params = {'instance_href' => instance_href} + params.deep_merge!({'backup' => {'name' => name}}) if name + params.deep_merge!({'backup' => {'description' => description}}) if description + location = connection.post(uri.path + "/restore", params) + Task.new('href' => location) + end +end diff --git a/lib/rest_connection/rightscale/child_account.rb b/lib/rest_connection/rightscale/child_account.rb new file mode 100644 index 0000000..77bb8a0 --- /dev/null +++ b/lib/rest_connection/rightscale/child_account.rb @@ -0,0 +1,41 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# + +# +# ChildAccount Resource requires 'enterprise_manager' role and 'enterprise_master' account setting +# + +class ChildAccount + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + + deny_methods :destroy, :update, :show + + def self.filters + [:name] + end +end diff --git a/lib/rest_connection/rightscale/cloud.rb b/lib/rest_connection/rightscale/cloud.rb new file mode 100644 index 0000000..0c46d02 --- /dev/null +++ b/lib/rest_connection/rightscale/cloud.rb @@ -0,0 +1,40 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +class Cloud + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + + deny_methods :create, :destroy, :update + + def self.filters + [:description, :name] + end + + def cloud_id + self.href.split("/").last + end +end diff --git a/lib/rest_connection/rightscale/cloud_account.rb b/lib/rest_connection/rightscale/cloud_account.rb new file mode 100644 index 0000000..7af08e5 --- /dev/null +++ b/lib/rest_connection/rightscale/cloud_account.rb @@ -0,0 +1,47 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +class CloudAccount + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + + deny_methods :update + + def cloud_id + self.cloud.split("/").last + end + + def create(opts) + location = connection.post(self.resource_plural_name, self.resource_singular_name.to_sym => opts) + if location =~ /aws/ + return "AWS Cloud Added successfully" + else + newrecord = self.new('links' => [ {'rel' => 'self', 'href' => location } ]) + newrecord.reload + return newrecord + end + end +end diff --git a/lib/rest_connection/rightscale/credential.rb b/lib/rest_connection/rightscale/credential.rb index 5ea8f05..69c2b68 100644 --- a/lib/rest_connection/rightscale/credential.rb +++ b/lib/rest_connection/rightscale/credential.rb @@ -1,19 +1,29 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ +# +# API 1.0 +# class Credential include RightScale::Api::Base extend RightScale::Api::BaseExtend diff --git a/lib/rest_connection/rightscale/deployment.rb b/lib/rest_connection/rightscale/deployment.rb index fa8679a..f01ef6b 100644 --- a/lib/rest_connection/rightscale/deployment.rb +++ b/lib/rest_connection/rightscale/deployment.rb @@ -1,21 +1,57 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ -class Deployment +# +# API 1.0 +# +class Deployment include RightScale::Api::Base extend RightScale::Api::BaseExtend + include RightScale::Api::Taggable + extend RightScale::Api::TaggableExtend + + def self.filters + [:description, :nickname] + end + + def reload + uri = URI.parse(self.href) + @params ? @params.merge!(connection.get(uri.path)) : @params = connection.get(uri.path) + @params['cloud_id'] = cloud_id + @params + end + + def self.create(opts) + location = connection.post(self.resource_plural_name, self.resource_singular_name.to_sym => opts) + newrecord = self.new('href' => location) + newrecord.reload + newrecord + end + + def set_inputs(hash = {}) + deploy_href = URI.parse(self.href) + connection.put(deploy_href.path, :deployment => {:parameters => hash }) + end def set_input(name, value) deploy_href = URI.parse(self.href) @@ -23,11 +59,11 @@ def set_input(name, value) end def servers_no_reload - server_list = [] - @params['servers'].each do |s| - server_list << Server.new(s) + connection.logger("WARNING: No Servers in the Deployment!") if @params['servers'].empty? + unless @params['servers'].reduce(true) { |bool,s| bool && s.is_a?(ServerInterface) } + @params['servers'].map! { |s| ServerInterface.new(self.cloud_id, s, self.rs_id) } end - return server_list + @params['servers'] end def servers @@ -45,4 +81,22 @@ def clone deploy_href = URI.parse(self.href) Deployment.new(:href => connection.post(deploy_href.path + "/duplicate")) end + + def destroy(wait_for_servers = nil) + deploy_href = URI.parse(self.href) + if wait_for_servers + servers_no_reload.each { |s| s.wait_for_state("stopped") } + end + connection.delete(deploy_href.path) + end + + def start_all + deploy_href = URI.parse(self.href) + connection.post(deploy_href.path + "/start_all") + end + + def stop_all + deploy_href = URI.parse(self.href) + connection.post(deploy_href.path + "/stop_all") + end end diff --git a/lib/rest_connection/rightscale/ec2_ebs_snapshot.rb b/lib/rest_connection/rightscale/ec2_ebs_snapshot.rb index ab6654d..84fa921 100644 --- a/lib/rest_connection/rightscale/ec2_ebs_snapshot.rb +++ b/lib/rest_connection/rightscale/ec2_ebs_snapshot.rb @@ -1,20 +1,32 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ +# +# API 1.0 +# class Ec2EbsSnapshot include RightScale::Api::Base extend RightScale::Api::BaseExtend + include RightScale::Api::Taggable + extend RightScale::Api::TaggableExtend end diff --git a/lib/rest_connection/rightscale/ec2_ebs_volume.rb b/lib/rest_connection/rightscale/ec2_ebs_volume.rb index 2c4feaf..55b2bda 100644 --- a/lib/rest_connection/rightscale/ec2_ebs_volume.rb +++ b/lib/rest_connection/rightscale/ec2_ebs_volume.rb @@ -1,24 +1,40 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ +# +# API 1.0 +# class Ec2EbsVolume include RightScale::Api::Base extend RightScale::Api::BaseExtend + include RightScale::Api::Taggable + extend RightScale::Api::TaggableExtend def attach(params) - connection.post('component_ec2_ebs_volumes.js' , :component_ec2_ebs_volume => params) + @link = ServerEc2EbsVolume.create(params) + end + + def detach + @link.destroy if @link end end diff --git a/lib/rest_connection/rightscale/ec2_elastic_ip.rb b/lib/rest_connection/rightscale/ec2_elastic_ip.rb index 157f030..4882bdf 100644 --- a/lib/rest_connection/rightscale/ec2_elastic_ip.rb +++ b/lib/rest_connection/rightscale/ec2_elastic_ip.rb @@ -1,20 +1,32 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ +# +# API 1.0 +# class Ec2ElasticIp include RightScale::Api::Base extend RightScale::Api::BaseExtend + + deny_methods :update end diff --git a/lib/rest_connection/rightscale/ec2_security_group.rb b/lib/rest_connection/rightscale/ec2_security_group.rb index ead9e01..76160d7 100644 --- a/lib/rest_connection/rightscale/ec2_security_group.rb +++ b/lib/rest_connection/rightscale/ec2_security_group.rb @@ -1,19 +1,63 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ -class Ec2SecurityGroup +# +# API 1.0 +# +class Ec2SecurityGroup include RightScale::Api::Base extend RightScale::Api::BaseExtend + + VALID_RULE_TYPES = [ + [:group, :owner], + [:cidr_ips, :from_port, :protocol, :to_port], + [:from_port, :group, :owner, :protocol, :to_port], + ] + + # NOTE - Create, Destroy, and Update require "security_manager" permissions + # NOTE - Can't remove rules, can only add + def add_rule(opts={}) + rule = {} + opts.each { |k,v| rule["#{k}".to_sym] = v } + + unless validate_rule(rule) + raise ArgumentError.new("add_rule expects one of these valid rule types: #{VALID_RULE_TYPES.to_json}") + end + + params = {:ec2_security_group => rule} + uri = URI.parse(self.href) + connection.put(uri.path, params) + + self.reload + end + + def validate_rule(rule) + VALID_RULE_TYPES.each do |valid_rule_type| + if rule.keys.sort_by {|sym| sym.to_s} == valid_rule_type.sort_by {|sym| sym.to_s} + return true + end + end + + false + end end diff --git a/lib/rest_connection/rightscale/ec2_server_array.rb b/lib/rest_connection/rightscale/ec2_server_array.rb index a28a0b9..521659f 100644 --- a/lib/rest_connection/rightscale/ec2_server_array.rb +++ b/lib/rest_connection/rightscale/ec2_server_array.rb @@ -1,21 +1,40 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ -class Ec2ServerArray +# +# API 1.0 +# +class Ec2ServerArray include RightScale::Api::Base extend RightScale::Api::BaseExtend + include RightScale::Api::Taggable + extend RightScale::Api::TaggableExtend + + attr_accessor :internal + + def initialize(*args, &block) + super(*args, &block) + end # Example: # right_script = @server_template.executables.first @@ -23,7 +42,7 @@ class Ec2ServerArray def run_script_on_all(script, server_template_hrefs, inputs=nil) serv_href = URI.parse(self.href) options = Hash.new - options[:ec2_server_array] = Hash.new + options[:ec2_server_array] = Hash.new options[:ec2_server_array][:right_script_href] = script.href options[:ec2_server_array][:parameters] = inputs unless inputs.nil? options[:ec2_server_array][:server_template_hrefs] = server_template_hrefs @@ -31,13 +50,44 @@ def run_script_on_all(script, server_template_hrefs, inputs=nil) connection.post("#{serv_href.path}/run_script_on_all", options) end + # + # Run a script on individual instances in a ServerArray + # + # This was formerly located in Ec2ServerArrayInternal but has + # been moved here to Ec2ServerArray as the call has been ported + # from API 0.1 to API 1.0. + # + # Example: array.run_script_on_instances(right_script, server_href, options_hash) + # + def run_script_on_instances(script, ec2_instance_hrefs=[], opts={}) + uri = URI.parse(self.href) + case script + when Executable then script = script.right_script + when String then script = RightScript.new('href' => script) + end + + params = {:right_script_href => script.href } + unless ec2_instance_hrefs.nil? || ec2_instance_hrefs.empty? + params[:ec2_instance_hrefs] = ec2_instance_hrefs + end + unless opts.nil? || opts.empty? + params[:parameters] = opts + end + params = {:ec2_server_array => params} + status_array=[] + connection.post(uri.path + "/run_script_on_instances", params).map do |work_unit| + status_array.push Status.new('href' => work_unit) + end + return(status_array) + end + def instances serv_href = URI.parse(self.href) connection.get("#{serv_href.path}/instances") rescue [] # raise an error on self.href which we want, it'll just rescue on rackspace and return an empty array. - end - + end + def terminate_all serv_href = URI.parse(self.href) connection.post("#{serv_href.path}/terminate_all") diff --git a/lib/rest_connection/rightscale/ec2_ssh_key.rb b/lib/rest_connection/rightscale/ec2_ssh_key.rb index fb55f0b..87bdce2 100644 --- a/lib/rest_connection/rightscale/ec2_ssh_key.rb +++ b/lib/rest_connection/rightscale/ec2_ssh_key.rb @@ -1,19 +1,50 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ -class Ec2SshKey +# +# API 1.0 +# +class Ec2SshKey include RightScale::Api::Base extend RightScale::Api::BaseExtend + + deny_methods :index, :update + + attr_accessor :internal + + def self.create(opts) + create_opts = { self.resource_singular_name.to_sym => opts } + create_opts['cloud_id'] = opts['cloud_id'] if opts['cloud_id'] + location = connection.post(self.resource_plural_name, create_opts) + newrecord = self.new('href' => location) + newrecord.reload + newrecord + end + + def initialize(*args, &block) + super(*args, &block) + if RightScale::Api::api0_1? + @internal = Ec2SshKeyInternal.new(*args, &block) + end + end end diff --git a/lib/rest_connection/rightscale/ec2_ssh_key_internal.rb b/lib/rest_connection/rightscale/ec2_ssh_key_internal.rb index c9fbe99..d490bad 100644 --- a/lib/rest_connection/rightscale/ec2_ssh_key_internal.rb +++ b/lib/rest_connection/rightscale/ec2_ssh_key_internal.rb @@ -1,25 +1,37 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ -class Ec2SshKeyInternal +# +# API 0.1 +# +class Ec2SshKeyInternal include RightScale::Api::Base extend RightScale::Api::BaseExtend - include RightScale::Api::Internal extend RightScale::Api::InternalExtend + deny_methods :show, :create, :update, :destroy + def resource_plural_name "ec2_ssh_keys" end @@ -35,5 +47,4 @@ def self.resource_plural_name def self.resource_singular_name "ec2_ssh_key" end - end diff --git a/lib/rest_connection/rightscale/executable.rb b/lib/rest_connection/rightscale/executable.rb index 2cfc787..73c59fc 100644 --- a/lib/rest_connection/rightscale/executable.rb +++ b/lib/rest_connection/rightscale/executable.rb @@ -1,7 +1,35 @@ -class Executable +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.0 +# +class Executable include RightScale::Api::Base extend RightScale::Api::BaseExtend + deny_methods :index, :create, :destroy, :update + # executable can be EITHER a right_script or recipe # executable example params format: # can have recipes AND right_scripts @@ -9,8 +37,8 @@ class Executable # { :recipe => # :position => 12, # :apply => "operational", - # :right_script => { "href" => "http://blah", - # "name" => "blah" + # :right_script => { "href" => "http://blah", + # "name" => "blah" # ... # } @@ -37,7 +65,7 @@ def name end def href - if right_script? + if right_script? return right_script.href else #recipes do not have hrefs, only names diff --git a/lib/rest_connection/rightscale/instance.rb b/lib/rest_connection/rightscale/instance.rb index d802fe1..fd32f77 100644 --- a/lib/rest_connection/rightscale/instance.rb +++ b/lib/rest_connection/rightscale/instance.rb @@ -1,27 +1,39 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ -# This is an instance facing api and can only be used with -# an authentication URL normally found in the instance's userdata called -# RS_API_URL -class Instance +# +# API 1.0 +# + +# Requires instance token for authentication +class Instance include RightScale::Api::Base extend RightScale::Api::BaseExtend + include RightScale::Api::Taggable + extend RightScale::Api::TaggableExtend #def create_ebs_volume_from_snap(snap_aws_id) # connection.post('create_ebs_volume.js', :aws_id => snap_aws_id ) - #end + #end def attach_ebs_volume(params) connection.put('attach_ebs_volume.js', params) diff --git a/lib/rest_connection/rightscale/instance_type.rb b/lib/rest_connection/rightscale/instance_type.rb new file mode 100644 index 0000000..bbfd32d --- /dev/null +++ b/lib/rest_connection/rightscale/instance_type.rb @@ -0,0 +1,45 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +class InstanceType + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + + deny_methods :create, :destroy, :update + + def self.parse_args(cloud_id) + "clouds/#{cloud_id}/" + end + + def self.filters + [:cpu_architecture, :description, :name, :resource_uid] + end + + def show + inst_href = URI.parse(self.href) + @params.merge! connection.get(inst_href.path, 'view' => "default") + end +end diff --git a/lib/rest_connection/rightscale/macro.rb b/lib/rest_connection/rightscale/macro.rb new file mode 100644 index 0000000..d5ff222 --- /dev/null +++ b/lib/rest_connection/rightscale/macro.rb @@ -0,0 +1,30 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.0 +# +class Macro + include RightScale::Api::Base + extend RightScale::Api::BaseExtend +end diff --git a/lib/rest_connection/rightscale/mc_audit_entry.rb b/lib/rest_connection/rightscale/mc_audit_entry.rb new file mode 100644 index 0000000..72ae833 --- /dev/null +++ b/lib/rest_connection/rightscale/mc_audit_entry.rb @@ -0,0 +1,109 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +class McAuditEntry + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + + deny_methods :destroy, :index + + def resource_plural_name + "audit_entries" + end + + def resource_singular_name + "audit_entry" + end + + def self.resource_plural_name + "audit_entries" + end + + def self.resource_singular_name + "audit_entry" + end + + def self.filters + [:auditee_href, :user_email] + end + + def self.find_all(start_date=nil, end_date=nil, limit=1000) + start_date ||= (Time.now.utc - (60*60*24*31)).strftime(RightScale::Api::DATETIME_FMT) + end_date ||= Time.now.utc.strftime(RightScale::Api::DATETIME_FMT) + index(start_date, end_date, limit) + end + + def self.find_with_filter(start_date, end_date, limit, filter) + index(start_date, end_date, limit, filter) + end + + def self.index(start_date, end_date, limit=1000, filter={}) + # Validate index params + ex_fmt = "2011/06/25 00:00:00 +0000" + regex = /^(\d{4})\/(\d{2})\/(\d{2}) (\d{2}):(\d{2}):(\d{2}) ([+-]\d{4})$/ + unless start_date =~ regex + raise ArgumentError.new("start_date doesn't match format. e.g., #{ex_fmt}") + end + unless end_date =~ regex + raise ArgumentError.new("end_date doesn't match format. e.g., #{ex_fmt}") + end + unless (1..1000) === limit.to_i + raise ArgumentError.new("limit is not within the range of 1..1000") + end + filter_params = [] + filter.each { |key,val| + unless self.filters.include?(key.to_sym) + raise ArgumentError.new("#{key} is not a valid filter for resource #{self.resource_singular_name}") + end + filter_params << "#{key}==#{val}" + } + + a = Array.new + url = self.resource_plural_name + if filter_params.empty? + connection.get(url).each do |object| + a << self.new(object) + end + else + connection.get(url, :filter => filter_params).each do |object| + a << self.new(object) + end + end + + return a + end + + def append(detail, offset) + uri = URI.parse(self.href) + connection.post(uri.path + "/append", 'detail' => detail, 'offset' => offset) + end + + def detail + uri = URI.parse(self.href) + res = connection.post(uri.path + "/detail") + return res.body + end +end diff --git a/lib/rest_connection/rightscale/mc_datacenter.rb b/lib/rest_connection/rightscale/mc_datacenter.rb new file mode 100644 index 0000000..f3d6a87 --- /dev/null +++ b/lib/rest_connection/rightscale/mc_datacenter.rb @@ -0,0 +1,67 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +class McDatacenter + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + + deny_methods :create, :destroy, :update + + def resource_plural_name + "datacenters" + end + + def resource_singular_name + "datacenter" + end + + def self.resource_plural_name + "datacenters" + end + + def self.resource_singular_name + "datacenter" + end + + def self.parse_args(cloud_id) + "clouds/#{cloud_id}/" + end + + def self.filters + [:name, :resource_uid] + end + + def show + inst_href = URI.parse(self.href) + @params.merge! connection.get(inst_href.path, 'view' => "full") + end + + def save + inst_href = URI.parse(self.href) + connection.put(inst_href.path, @params) + end + +end diff --git a/lib/rest_connection/rightscale/mc_deployment.rb b/lib/rest_connection/rightscale/mc_deployment.rb new file mode 100644 index 0000000..a62e6ed --- /dev/null +++ b/lib/rest_connection/rightscale/mc_deployment.rb @@ -0,0 +1,72 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +class McDeployment + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + include RightScale::Api::McTaggable + extend RightScale::Api::McTaggableExtend + include RightScale::Api::McInput + + def resource_plural_name + "deployments" + end + + def resource_singular_name + "deployment" + end + + def self.resource_plural_name + "deployments" + end + + def self.resource_singular_name + "deployment" + end + + def self.filters + [:description, :name] + end + + def self.create(opts) + location = connection.post(resource_plural_name, opts) + newrecord = self.new('href' => location) + newrecord.show + newrecord + end + + def save + inst_href = URI.parse(self.href) + connection.put(inst_href.path, @params) + end + + # TODO Add server method + + def destroy + deploy_href = URI.parse(self.href) + connection.delete(deploy_href.path) + end +end diff --git a/lib/rest_connection/rightscale/mc_image.rb b/lib/rest_connection/rightscale/mc_image.rb new file mode 100644 index 0000000..0a1c5f5 --- /dev/null +++ b/lib/rest_connection/rightscale/mc_image.rb @@ -0,0 +1,56 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +class McImage + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + + deny_methods :create, :destroy, :update + + def resource_plural_name + "images" + end + + def resource_singular_name + "image" + end + + def self.resource_plural_name + "images" + end + + def self.resource_singular_name + "image" + end + + def self.filters + [:cpu_architecture, :description, :image_type, :name, :os_platform, :resource_uid, :visibility] + end + + def self.parse_args(cloud_id) + "clouds/#{cloud_id}/" + end +end diff --git a/lib/rest_connection/rightscale/mc_instance.rb b/lib/rest_connection/rightscale/mc_instance.rb new file mode 100644 index 0000000..7fcb8ba --- /dev/null +++ b/lib/rest_connection/rightscale/mc_instance.rb @@ -0,0 +1,204 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +class McInstance + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + include RightScale::Api::McTaggable + extend RightScale::Api::McTaggableExtend + include RightScale::Api::McInput + attr_accessor :monitoring_metrics + + deny_methods :create, :destroy + + def resource_plural_name + "instances" + end + + def resource_singular_name + "instance" + end + + def self.resource_plural_name + "instances" + end + + def self.resource_singular_name + "instance" + end + + def self.parse_args(cloud_id) + "clouds/#{cloud_id}/" + end + + def self.filters + [ + :datacenter_href, + :deployment_href, + :name, + :os_platform, + :parent_href, + :private_dns_name, + :private_ip_address, + :public_dns_name, + :public_ip_address, + :resource_uid, + :server_template_href, + :state + ] + end + + def show + inst_href = URI.parse(self.href) + @params.merge! connection.get(inst_href.path, 'view' => "full") + end + + def save + update + end + + def map_security_groups(to, sg_ary) + sg_ary.map { |hsh| hsh["href"] } + end + + def map_user_data(to, user_data) + user_data + end + + def update + fields = [{"attr" => :datacenter, "api" => :datacenter_href}, + {"attr" => :image, "api" => :image_href}, + {"attr" => :instance_type, "api" => :instance_type_href}, + { "api" => :kernel_image_href}, + {"attr" => :multi_cloud_image, "api" => :multi_cloud_image_href}, + { "api" => :ramdisk_image_href}, + {"attr" => :security_groups, "fn" => :map_security_groups, "api" => :security_group_hrefs}, + {"attr" => :server_template, "api" => :server_template_href}, + {"attr" => :ssh_key, "api" => :ssh_key_href}, + {"attr" => :user_data, "fn" => :map_user_data, "api" => :user_data}] + + opts = {"instance" => {}} + instance = opts["instance"] + to = "api" + from = "attr" + fields.each { |hsh| + next unless hsh[from] + val = self[hsh[from]] + if hsh["fn"] + instance[hsh[to].to_s] = __send__(hsh["fn"], to, val) unless val.nil? || val.empty? + else + instance[hsh[to].to_s] = val unless val.nil? || val.empty? + end + } + inst_href = URI.parse(self.href) + connection.put(inst_href.path, opts) + end + + def launch + inst_href = URI.parse(self.href) + connection.post(inst_href.path + '/launch') + end + + def terminate + inst_href = URI.parse(self.href) + connection.post(inst_href.path + '/terminate') + end + + def multi_update(input_ary) + inst_href = URI.parse(self.href) + connection.put(inst_href.path + '/inputs/multi_update', {'inputs' => input_ary}) + end + + def transform_inputs(sym, parameters) + ret = nil + if parameters.is_a?(Array) and sym == :to_h + ret = {} + parameters.each { |hash| ret[hash['name']] = hash['value'] } + elsif parameters.is_a?(Hash) and sym == :to_a + ret = [] + parameters.each { |key,val| ret << {'name' => key, 'value' => val} } + end + ret + end + + def translate_href(old_href) + href = old_href.dup + href.gsub!(/ec2_/,'') + href.gsub!(/\/acct\/[0-9]*/,'') + return href + end + + def run_executable(executable, opts=nil, ignore_lock=false) + run_options = Hash.new + if executable.is_a?(Executable) + if executable.recipe? + run_options[:recipe_name] = executable.recipe + else + run_options[:right_script_href] = translate_href(executable.right_script.href) + end + elsif executable.is_a?(RightScript) + run_options[:right_script_href] = translate_href(executable.href) + else + raise "Invalid class passed to run_executable, needs Executable or RightScript, was:#{executable.class}" + end + + if not opts.nil? and opts.has_key?(:ignore_lock) + run_options[:ignore_lock] = "true" + opts.delete(:ignore_lock) + opts = nil if opts.empty? + end + + inst_href = URI.parse(self.href) + run_options[:inputs] = transform_inputs(:to_a, opts) unless opts.nil? + run_options[:ignore_lock] = "true" if ignore_lock + location = connection.post(inst_href.path + '/run_executable', run_options) + Task.new('href' => location) + end + + def fetch_monitoring_metrics + @monitoring_metrics = [] + return @monitoring_metrics if self.state != "operational" + connection.get(URI.parse(self.href).path + '/monitoring_metrics').each { |mm| + @monitoring_metrics << MonitoringMetric.new(mm) + } + @monitoring_metrics + end + + def get_sketchy_data(params) + metric = fetch_monitoring_metrics.detect { |mm| mm.plugin == params['plugin_name'] and mm.view == params['plugin_type'] } + raise "Metric not found!" unless metric + metric.data(params['start'], params['end']) + end + + def get_data(params) + get_sketchy_data(params) + end + + def reboot + self.show + connection.post(URI.parse(self.href).path + '/reboot') + end +end diff --git a/lib/rest_connection/rightscale/mc_instance_type.rb b/lib/rest_connection/rightscale/mc_instance_type.rb new file mode 100644 index 0000000..25c9450 --- /dev/null +++ b/lib/rest_connection/rightscale/mc_instance_type.rb @@ -0,0 +1,61 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +class McInstanceType + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + + deny_methods :create, :destroy, :update + + def resource_plural_name + "instance_types" + end + + def resource_singular_name + "instance_type" + end + + def self.resource_plural_name + "instance_types" + end + + def self.resource_singular_name + "instance_type" + end + + def self.parse_args(cloud_id) + "clouds/#{cloud_id}/" + end + + def self.filters + [:cpu_architecture, :description, :name, :resource_uid] + end + + def show + inst_href = URI.parse(self.href) + @params.merge! connection.get(inst_href.path, 'view' => "default") + end +end diff --git a/lib/rest_connection/rightscale/mc_multi_cloud_image.rb b/lib/rest_connection/rightscale/mc_multi_cloud_image.rb new file mode 100644 index 0000000..a9cface --- /dev/null +++ b/lib/rest_connection/rightscale/mc_multi_cloud_image.rb @@ -0,0 +1,80 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +class McMultiCloudImage + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + include RightScale::Api::McTaggable + extend RightScale::Api::McTaggableExtend + + deny_methods :update #supported in API, not imp'd in code yet + + def resource_plural_name + "multi_cloud_images" + end + + def resource_singular_name + "multi_cloud_image" + end + + def self.resource_plural_name + "multi_cloud_images" + end + + def self.resource_singular_name + "multi_cloud_image" + end + + def self.parse_args(server_template_id=nil) + server_template_id ? "server_templates/#{server_template_id}/" : "" + end + + def self.filters + [:description, :name, :revision] + end + + def supported_cloud_ids + settings.map { |mcics| mcics.cloud_id } + end + + def reload + @settings = nil + super + end + + # Note, only returns API 1.5 clouds, API 1.0 omitted + def settings + return @settings if @settings + @settings = [] + url = URI.parse(self.href) + connection.get(url.path + '/settings').each { |s| + @settings << McMultiCloudImageSetting.new(s) + } + @settings + end + def get_settings; settings; end + +end diff --git a/lib/rest_connection/rightscale/mc_multi_cloud_image_setting.rb b/lib/rest_connection/rightscale/mc_multi_cloud_image_setting.rb new file mode 100644 index 0000000..97a10f5 --- /dev/null +++ b/lib/rest_connection/rightscale/mc_multi_cloud_image_setting.rb @@ -0,0 +1,67 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +class McMultiCloudImageSetting + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + + deny_methods :update #supported in API, not imp'd in code yet + + def resource_plural_name + "settings" + end + + def resource_singular_name + "setting" + end + + def self.resource_plural_name + "settings" + end + + def self.resource_singular_name + "setting" + end + + def self.parse_args(multi_cloud_image_id) + "multi_cloud_images/#{multi_cloud_image_id}/" + end + + def self.filters + [:cloud_href, :multi_cloud_image_href] + end + + def cloud_id + self.cloud.split(/\//).last.to_i + end + + # API 1.5 MultiCloudImageSetting is posted to url + # /api/multi_cloud_images/:id/settings but the object it posts to the + # API is named :multi_cloud_image_setting => { attrs } + def self.resource_post_name + "multi_cloud_image_setting" + end +end diff --git a/lib/rest_connection/rightscale/mc_security_group.rb b/lib/rest_connection/rightscale/mc_security_group.rb new file mode 100644 index 0000000..ae668c5 --- /dev/null +++ b/lib/rest_connection/rightscale/mc_security_group.rb @@ -0,0 +1,116 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +class McSecurityGroup + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + + deny_methods :update + + def resource_plural_name + "security_groups" + end + + def resource_singular_name + "security_group" + end + + def self.resource_plural_name + "security_groups" + end + + def self.resource_singular_name + "security_group" + end + + def self.parse_args(cloud_id) + "clouds/#{cloud_id}/" + end + + def self.filters + [:name, :resource_uid] + end + + # NOTE: Create & Destroy require "security_manager" permissions + def self.create(cloud_id, opts={}) + url = "#{parse_args(cloud_id)}#{self.resource_plural_name}" + location = connection.post(url, self.resource_singular_name.to_sym => opts) + newrecord = self.new('links' => [ {'rel' => 'self', 'href' => location } ]) + + rules = opts[:rules] || opts["rules"] + [rules].flatten.each { |rule_hash| newrecord.add_rule(rule_hash) } if rules + + newrecord.reload + newrecord + end + + def rules + self.load(SecurityGroupRule) + end + + def add_rule(opts={}) + opts.each { |k,v| opts["#{k}".to_sym] = v } + fields = [ + {"1.0" => :owner, "1.5" => :group_owner}, # optional + {"1.0" => :group, "1.5" => :group_name}, # optional + {"1.0" => :cidr_ip, "1.5" => :cidr_ips}, # optional + {"1.0" => :protocol, "1.5" => :protocol}, # "tcp" || "udp" || "icmp" + {"1.0" => :from_port, "1.5" => :start_port}, # optional + {"1.0" => :to_port, "1.5" => :end_port}, # optional + { "1.5" => :source_type}, # "cidr_ips" || "group" + { "1.5" => :icmp_code}, # optional + { "1.5" => :icmp_type}, # optional + { "1.5" => :security_group_href}, # optional + ] + unless opts[:protocol] + raise ArgumentError.new("add_rule requires the 'protocol' option") + end + params = { + :source_type => ((opts[:cidr_ip] || opts[:cidr_ips]) ? "cidr_ips" : "group"), + :security_group_href => self.href, + :protocol_details => {} + } + + fields.each { |ver| + next unless val = opts[ver["1.0"]] || opts[ver["1.5"]] + if ver["1.5"].to_s =~ /port|icmp/ + params[:protocol_details][ver["1.5"]] = val + else + params[ver["1.5"]] = val + end + } + + SecurityGroupRule.create(params) + end + + def remove_rules_by_filters(filters={}) + rules_to_delete = rules + filters.each do |filter,regex| + @rules.reject! { |rule| rule[filter] =~ Regexp.new(regex) } + end + @rules.each { |rule| rule.destroy } + end +end diff --git a/lib/rest_connection/rightscale/mc_server.rb b/lib/rest_connection/rightscale/mc_server.rb index 9f87f9a..d490222 100644 --- a/lib/rest_connection/rightscale/mc_server.rb +++ b/lib/rest_connection/rightscale/mc_server.rb @@ -1,25 +1,35 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . - -# -# You must have Beta v1.5 API access to use these internal API calls. -# -class McServer +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ +# +# API 1.5 +# +class McServer < Server include RightScale::Api::Gateway extend RightScale::Api::GatewayExtend - + include RightScale::Api::McTaggable + extend RightScale::Api::McTaggableExtend + attr_accessor :current_instance, :next_instance, :inputs + def resource_plural_name "servers" end @@ -35,5 +45,309 @@ def self.resource_plural_name def self.resource_singular_name "server" end - + + def self.parse_args(deployment_id=nil) + deployment_id ? "deployments/#{deployment_id}/" : "" + end + + def self.filters + [:deployment_href, :name] + end + + def launch + if actions.include?("launch") + t = URI.parse(self.href) + begin + connection.post(t.path + '/launch') + rescue Exception => e + if connection.settings[:azure_hack_on] + puts "**** [AZURE_HACK is ON] - McServer.launch() nickname: #{nickname}, caught exception #{e.message}" + puts "**** connection.settings[:azure_hack_retry_count] = #{connection.settings[:azure_hack_retry_count]}" + puts "**** connection.settings[:azure_hack_sleep_seconds] = #{connection.settings[:azure_hack_sleep_seconds]}" + + # 504 Gateway should always be treated as a successful launch + target_504_gateway_timeout_error_message = "504 Gateway Time-out" + + # All 422 exceptions should be retried + target_422_error_message = "Invalid response HTTP code: 422:" + + if e.message =~ /#{target_504_gateway_timeout_error_message}/ + exception_matched_message = "**** McServer.launch(): Caught #{e.message}, treating as a successful launch..." + puts(exception_matched_message) + connection.logger(exception_matched_message) + true + elsif e.message =~ /#{target_422_error_message}/ + azure_hack_retry_count = connection.settings[:azure_hack_retry_count] + exception_matched_message = "**** McServer.launch(): Caught #{e.message}, retrying launch..." + puts(exception_matched_message) + connection.logger(exception_matched_message) + + retry_count = 1 + loop do + # sleep for azure_hack_sleep_seconds seconds + sleep_message = "**** McServer.launch(): Sleeping for #{connection.settings[:azure_hack_sleep_seconds]} seconds and then retrying (#{retry_count}) launch..." + puts(sleep_message) + connection.logger(sleep_message) + sleep(connection.settings[:azure_hack_sleep_seconds]) + + # retry the launch + begin + connection.post(t.path + '/launch') + rescue Exception => e2 + exception_caught_message = "**** McServer.launch(): Retry caught #{e2.message}..." + puts(exception_caught_message) + connection.logger(exception_caught_message) + + if e2.message =~ /#{target_422_error_message}/ + azure_hack_retry_count -= 1 + if azure_hack_retry_count > 0 + retry_count += 1 + + # Try again on next iteration + next + else + # Azure Hack maximum retries exceeded so rethrow the new 422 exception + raise + end + else + # On this re-launch we got some other exception so rethrow it + raise + end + end + + # Fell through so launch worked and we need to break out of the retry do loop + break + end + else + # Didn't match on any target exception so rethrow the original exception + raise + end + else + # Azure Hack isn't enabled so rethrow the original exception + raise + end + end + elsif self.state == "inactive" + raise "FATAL: Server is in an unlaunchable state!" + else + connection.logger("WARNING: was in #{self.state} so skipping launch call") + end + end + + def terminate + if actions.include?("terminate") + t = URI.parse(self.href) + connection.post(t.path + '/terminate') + @current_instance = nil +# elsif self.state != "inactive" +# raise "FATAL: Server is in an interminable state!" + else + connection.logger("WARNING: was in #{self.state} so skipping terminate call") + end + end + + def force_terminate + t = URI.parse(self.href) + connection.post(t.path + '/terminate') + connection.post(t.path + '/terminate') + @current_instance = nil + end + + def start #start_ebs + raise "You shouldn't be here." + end + + def stop #stop_ebs + raise "You shouldn't be here." + end + + def run_executable(executable, opts=nil, ignore_lock=false) + raise "Instance isn't running; Can't run executable" unless @current_instance + @current_instance.run_executable(executable, opts, ignore_lock) + end + + def transform_inputs(sym, parameters) + ret = nil + if parameters.is_a?(Array) and sym == :to_h + ret = {} + parameters.each { |hash| ret[hash['name']] = hash['value'] } + elsif parameters.is_a?(Hash) and sym == :to_a + ret = [] + parameters.each { |key,val| ret << {'name' => key, 'value' => val} } + end + ret + end + + def inputs + if @current_instance + @current_instance.show + return transform_inputs(:to_h, @current_instance.inputs) + else + @next_instance.show + return transform_inputs(:to_h, @next_instance.inputs) + end + end + + def set_input(name, value) + settings unless @next_instance + @current_instance.multi_update([{'name' => name, 'value' => value}]) if @current_instance + @next_instance.multi_update([{'name' => name, 'value' => value}]) + end + + def set_current_inputs(hash = {}) + settings unless @next_instance + @current_instance.multi_update(transform_inputs(:to_a, hash)) if @current_instance + end + + def set_next_inputs(hash = {}) + settings unless @next_instance + @next_instance.multi_update(transform_inputs(:to_a, hash)) + end + + + def settings #show + serv_href = URI.parse(self.href) + @params = connection.get(serv_href.path, 'view' => 'instance_detail') + if self['current_instance'] + @current_instance = McInstance.new(self['current_instance']) + @current_instance.show + end + @next_instance = McInstance.new(self['next_instance']) + @next_instance.show + @params + end + + def monitoring + ret = @current_instance.fetch_monitoring_metrics + raise "FATAL: Monitoring not available!" if ret.empty? + ret + end + + # *timeout <~Integer> optional, how long to wait for the inactive state before declare failure (in seconds). + def relaunch(timeout=1200) + self.terminate + self.wait_for_state("inactive", timeout) + self.launch + end + + # Attributes taken for granted in API 1.0 + def server_type + "gateway" + end + + def server_template_href + if @current_instance + return @current_instance.server_template + end + self.settings unless @next_instance + return @next_instance.server_template + end + + def deployment_href + hash_of_links["deployment"] + end + + def current_instance_href + hash_of_links["current_instance"] + end + + def cloud_id + settings unless @next_instance + cloud_href = @current_instance.hash_of_links["cloud"] if @current_instance + cloud_href = @next_instance.hash_of_links["cloud"] unless cloud_href + return cloud_href.split("/").last.to_i + end + + def dns_name + self.settings + ret = nil + if @current_instance + ret ||= @current_instance.public_ip_addresses.first + ret ||= @current_instance.public_dns_names.first + ret ||= get_tags_by_namespace("server")["current_instance"]["public_ip_0"] + end + ret + end + + def private_ip + self.settings + ret = nil + if @current_instance + ret ||= @current_instance.private_ip_addresses.first + ret ||= @current_instance.private_dns_names.first + ret ||= get_tags_by_namespace("server")["current_instance"]["private_ip_0"] + end + ret + end + + def save + update + end + + def update + @next_instance.update + @current_instance.update if @current_instance + end + + def reload_as_current + settings # Gets all instance (including current) information + end + + def reload_as_next + settings # Gets all instance (including current) information + end + + def get_sketchy_data(params) + settings + raise "No current instance found!" unless @current_instance + @current_instance.get_sketchy_data(params) + end + + # Override Taggable mixin so that it sets tags on both next and current instances + def current_tags(reload=true) + ret = [] + if @current_instance + ret = McTag.search_by_href(self.current_instance_href).first["tags"].map { |h| h["name"] } + end + ret + end + + def add_tags(*args) + return false if args.empty? + args.uniq! + McTag.set(self.href, args) + McTag.set(self.current_instance_href, args) if @current_instance + self.tags(true) + end + + def remove_tags(*args) + return false if args.empty? + args.uniq! + McTag.unset(self.href, args) + McTag.unset(self.current_instance_href, args) if @current_instance + self.tags(true) + end + + def get_tags_by_namespace(namespace) + ret = {} + tags = {"self" => self.tags(true)} + tags["current_instance"] = self.current_tags if @current_instance + tags.each { |res,ary| + ret[res] ||= {} + ary.each { |tag| + next unless tag.start_with?("#{namespace}:") + key = tag.split("=").first.split(":")[1..-1].join(":") + value = tag.split(":")[1..-1].join(":").split("=")[1..-1].join("=") + ret[res][key] = value + } + } + return ret + end + + def clear_tags(namespace = nil) + tags = self.tags(true) + tags.deep_merge! self.current_tags if @current_instance + tags = tags.select { |tag| tag.start_with?("#{namespace}:") } if namespace + self.remove_tags(*tags) + end end diff --git a/lib/rest_connection/rightscale/mc_server_array.rb b/lib/rest_connection/rightscale/mc_server_array.rb new file mode 100644 index 0000000..018dd45 --- /dev/null +++ b/lib/rest_connection/rightscale/mc_server_array.rb @@ -0,0 +1,58 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +class McServerArray + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + include RightScale::Api::McTaggable + extend RightScale::Api::McTaggableExtend + + def resource_plural_name + "server_arrays" + end + + def resource_singular_name + "server_array" + end + + def self.resource_plural_name + "server_arrays" + end + + def self.resource_singular_name + "server_array" + end + + def self.parse_args(deployment_id=nil) + (deployment_id ? "deployments/#{deployment_id}/" : "") + end + + def self.filters + [:deployment_href, :name] + end + + # TODO +end diff --git a/lib/rest_connection/rightscale/mc_server_template.rb b/lib/rest_connection/rightscale/mc_server_template.rb new file mode 100644 index 0000000..49735f8 --- /dev/null +++ b/lib/rest_connection/rightscale/mc_server_template.rb @@ -0,0 +1,111 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +class McServerTemplate + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + include RightScale::Api::McTaggable + extend RightScale::Api::McTaggableExtend + include RightScale::Api::McInput + + def resource_plural_name + "server_templates" + end + + def resource_singular_name + "server_template" + end + + def self.resource_plural_name + "server_templates" + end + + def self.resource_singular_name + "server_template" + end + + def self.filters + [:description, :multi_cloud_image_href, :name, :revision] + end + + def get_mcis_and_settings + @params["multi_cloud_images"] = McMultiCloudImage.find_all(self.rs_id) + @params["multi_cloud_images"].each { |mci| mci.settings } # Eager load + @mci_links = McServerTemplateMultiCloudImage.find_with_filter(:server_template_href => self.href) + end + + def multi_cloud_images + unless @params["multi_cloud_images"] + get_mcis_and_settings + end + @params["multi_cloud_images"] + end + + def add_multi_cloud_image(mci_href) + @mci_links = McServerTemplateMultiCloudImage.find_with_filter(:server_template_href => self.href) + if @mci_links.detect { |mci_link| mci_link.multi_cloud_image == mci_href } + connection.logger("WARNING: MCI #{mci_href} is already attached") + else + ret = McServerTemplateMultiCloudImage.create(:multi_cloud_image_href => mci_href, + :server_template_href => self.href) + get_mcis_and_settings + ret + end + end + + def detach_multi_cloud_image(mci_href) + @mci_links = McServerTemplateMultiCloudImage.find_with_filter(:server_template_href => self.href) + if link = @mci_links.detect { |mci_link| mci_link.multi_cloud_image == mci_href } + ret = link.destroy + get_mcis_and_settings + ret + else + connection.logger("WARNING: MCI #{mci_href} is not attached") + end + end + + def set_default_multi_cloud_image(mci_href) + @mci_links = McServerTemplateMultiCloudImage.find_with_filter(:server_template_href => self.href) + if link = @mci_links.detect { |mci_link| mci_link.multi_cloud_image == mci_href } + ret = link.make_default + get_mcis_and_settings + ret + else + connection.logger("WARNING: MCI #{mci_href} is not attached") + end + end + + def commit(message, commit_head_dependencies, freeze_repositories) + options = {:commit_message => "#{message}", + :commit_head_dependencies => (commit_head_dependencies && true), + :freeze_repositories => (freeze_repositories && true)} + t = URI.parse(self.href) + location = connection.post(t.path + "/commit", options) + newrecord = McServerTemplate.new('links' => [ {'rel' => 'self', 'href' => location } ]) + newrecord.reload + newrecord + end +end diff --git a/lib/rest_connection/rightscale/mc_server_template_multi_cloud_image.rb b/lib/rest_connection/rightscale/mc_server_template_multi_cloud_image.rb new file mode 100644 index 0000000..29d7c13 --- /dev/null +++ b/lib/rest_connection/rightscale/mc_server_template_multi_cloud_image.rb @@ -0,0 +1,58 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# + +class McServerTemplateMultiCloudImage + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + + deny_methods :update + + def resource_plural_name + "server_template_multi_cloud_images" + end + + def resource_singular_name + "server_template_multi_cloud_image" + end + + def self.resource_plural_name + "server_template_multi_cloud_images" + end + + def self.resource_singular_name + "server_template_multi_cloud_image" + end + + def self.filters + [:is_default, :multi_cloud_image_href, :server_template_href] + end + + def make_default + my_href = URI.parse(self.href) + connection.post(my_href + "/make_default") + end +end diff --git a/lib/rest_connection/rightscale/mc_ssh_key.rb b/lib/rest_connection/rightscale/mc_ssh_key.rb new file mode 100644 index 0000000..fdc9a2f --- /dev/null +++ b/lib/rest_connection/rightscale/mc_ssh_key.rb @@ -0,0 +1,64 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +class McSshKey + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + + deny_methods :update + + def resource_plural_name + "ssh_keys" + end + + def resource_singular_name + "ssh_key" + end + + def self.resource_plural_name + "ssh_keys" + end + + def self.resource_singular_name + "ssh_key" + end + + def self.parse_args(cloud_id) + "clouds/#{cloud_id}/" + end + + def self.filters + [:resource_uid] + end + + def self.create(opts) + create_opts = { self.resource_singular_name.to_sym => opts } + location = connection.post("clouds/#{opts['cloud_id']}/#{self.resource_plural_name}", create_opts) + newrecord = self.new('href' => location) + newrecord.reload + newrecord + end +end diff --git a/lib/rest_connection/rightscale/mc_tag.rb b/lib/rest_connection/rightscale/mc_tag.rb new file mode 100644 index 0000000..60245d1 --- /dev/null +++ b/lib/rest_connection/rightscale/mc_tag.rb @@ -0,0 +1,79 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +class McTag + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + + deny_methods :index, :show, :create, :destroy, :update + + def resource_plural_name + "tags" + end + + def resource_singular_name + "tag" + end + + def self.resource_plural_name + "tags" + end + + def self.resource_singular_name + "tag" + end + + def self.search(resource_name, tags, opts=nil) #, include_tags_with_prefix = false) + parameters = { :resource_type => resource_name.to_s, :tags => tags } + parameters.merge!(opts) unless opts.nil? + result = connection.post("tags/by_tag", parameters) + end + + def self.search_by_href(*resource_hrefs) + connection.post("tags/by_resource", :resource_hrefs => resource_hrefs) + end + #TAGGABLE_RESOURCES = [ 'Server', 'Ec2EbsSnapshot', 'Ec2EbsVolume', 'Ec2Image', 'Image', 'ServerArray', 'Ec2Instance', + # 'Instance', 'Deployment', 'ServerTemplate', 'Ec2ServerTemplate' ] + # + # Tag.set( resource_href, tags ) where tags is an array of tags to set on the resource. + def self.multi_add(resource_hrefs, tags) + resource_hrefs = [resource_hrefs] unless resource_hrefs.is_a?(Array) + connection.post("tags/multi_add", :resource_hrefs => resource_hrefs, :tags => tags) + end + + def self.set(resource_hrefs, tags) + self.multi_add(resource_hrefs, tags) + end + + def self.multi_delete(resource_hrefs, tags) + resource_hrefs = [resource_hrefs] unless resource_hrefs.is_a?(Array) + connection.post("tags/multi_delete", :resource_hrefs => resource_hrefs, :tags => tags) + end + + def self.unset(resource_hrefs, tags) + self.multi_delete(resource_hrefs, tags) + end +end diff --git a/lib/rest_connection/rightscale/mc_volume.rb b/lib/rest_connection/rightscale/mc_volume.rb new file mode 100644 index 0000000..6e6039c --- /dev/null +++ b/lib/rest_connection/rightscale/mc_volume.rb @@ -0,0 +1,67 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +class McVolume + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + include RightScale::Api::McTaggable + extend RightScale::Api::McTaggableExtend + + deny_methods :update + + def resource_plural_name + "volumes" + end + + def resource_singular_name + "volume" + end + + def self.resource_plural_name + "volumes" + end + + def self.resource_singular_name + "volume" + end + + def self.parse_args(cloud_id) + "clouds/#{cloud_id}/" + end + + def self.filters + [:datacenter_href, :description, :name, :parent_volume_snapshot_href, :resource_uid] + end + + def show + inst_href = URI.parse(self.href) + @params.merge! connection.get(inst_href.path, 'view' => 'extended') + end + + def attachment + connection.get(self.current_volume_attachment) + end +end diff --git a/lib/rest_connection/rightscale/mc_volume_attachment.rb b/lib/rest_connection/rightscale/mc_volume_attachment.rb new file mode 100644 index 0000000..adf98ea --- /dev/null +++ b/lib/rest_connection/rightscale/mc_volume_attachment.rb @@ -0,0 +1,62 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +class McVolumeAttachment + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + + deny_methods :update + + def resource_plural_name + "volume_attachments" + end + + def resource_singular_name + "volume_attachment" + end + + def self.resource_plural_name + "volume_attachments" + end + + def self.resource_singular_name + "volume_attachment" + end + + def self.parse_args(cloud_id, instance_id=nil) + return "clouds/#{cloud_id}/" unless instance_id + return "clouds/#{cloud_id}/volumes/#{instance_id}/" if instance_id + end + + def self.filters + [:instance_href, :resource_uid, :volume_href] + end + + def show + inst_href = URI.parse(self.href) + @params.merge! connection.get(inst_href.path) + end +end diff --git a/lib/rest_connection/rightscale/mc_volume_snapshot.rb b/lib/rest_connection/rightscale/mc_volume_snapshot.rb new file mode 100644 index 0000000..097b14d --- /dev/null +++ b/lib/rest_connection/rightscale/mc_volume_snapshot.rb @@ -0,0 +1,69 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +class McVolumeSnapshot + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + include RightScale::Api::McTaggable + extend RightScale::Api::McTaggableExtend + + deny_methods :update + + def resource_plural_name + "volume_snapshots" + end + + def resource_singular_name + "volume_snapshot" + end + + def self.resource_plural_name + "volume_snapshots" + end + + def self.resource_singular_name + "volume_snapshot" + end + + def self.parse_args(cloud_id, volume_id=nil) + return "clouds/#{cloud_id}/" unless volume_id + return "clouds/#{cloud_id}/volumes/#{volume_id}/" if volume_id + end + + def self.filters + [:description, :name, :parent_volume_href, :resource_uid] + end + + def show + inst_href = URI.parse(self.href) + @params.merge! connection.get(inst_href.path) + end + + def save + inst_href = URI.parse(self.href) + connection.put(inst_href.path, @params) + end +end diff --git a/lib/rest_connection/rightscale/mc_volume_type.rb b/lib/rest_connection/rightscale/mc_volume_type.rb new file mode 100644 index 0000000..f6758a7 --- /dev/null +++ b/lib/rest_connection/rightscale/mc_volume_type.rb @@ -0,0 +1,61 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +class McVolumeType + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + + deny_methods :create, :destroy, :update + + def resource_plural_name + "volume_types" + end + + def resource_singular_name + "volume_type" + end + + def self.resource_plural_name + "volume_types" + end + + def self.resource_singular_name + "volume_type" + end + + def self.parse_args(cloud_id) + "clouds/#{cloud_id}/" + end + + def self.filters + [:name, :resource_uid] + end + + def show + inst_href = URI.parse(self.href) + @params.merge! connection.get(inst_href.path) + end +end diff --git a/lib/rest_connection/rightscale/monitoring_metric.rb b/lib/rest_connection/rightscale/monitoring_metric.rb new file mode 100644 index 0000000..3d3160e --- /dev/null +++ b/lib/rest_connection/rightscale/monitoring_metric.rb @@ -0,0 +1,49 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +class MonitoringMetric + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + + deny_methods :create, :destroy, :update + + def self.parse_args(cloud_id, instance_id) + "clouds/#{cloud_id}/instances/#{instance_id}/" + end + + def self.filters + [:plugin, :view] + end + + def data(start_time = "-60", end_time = "0") + params = {'start' => start_time.to_s, 'end' => end_time.to_s} + monitor = connection.get(URI.parse(self.href).path + "/data", params) + # NOTE: The following is a dirty hack + monitor['data'] = monitor['variables_data'].first + monitor['data']['value'] = monitor['data']['points'] + monitor + end +end diff --git a/lib/rest_connection/rightscale/multi_cloud_image.rb b/lib/rest_connection/rightscale/multi_cloud_image.rb index 6594a67..d8b5d87 100644 --- a/lib/rest_connection/rightscale/multi_cloud_image.rb +++ b/lib/rest_connection/rightscale/multi_cloud_image.rb @@ -1,19 +1,59 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ -class MultiCloudImage +# +# API 1.0 +# +class MultiCloudImage include RightScale::Api::Base extend RightScale::Api::BaseExtend + + deny_methods :create, :destroy, :update + + attr_accessor :internal + + def supported_cloud_ids + @params["multi_cloud_image_cloud_settings"].map { |mcics| mcics.cloud_id } + end + + # You must have access to multiple APIs for this (0.1, and 1.5) + def find_and_flatten_settings() + internal = MultiCloudImageInternal.new("href" => self.href) + internal.reload + total_image_count = internal.multi_cloud_image_cloud_settings.size + # The .settings call filters out non-ec2 images + more_settings = [] + if total_image_count > internal.settings.size + more_settings = McMultiCloudImage.find(rs_id.to_i).settings + end + @params["multi_cloud_image_cloud_settings"] = internal.settings + more_settings + end + + def initialize(*args, &block) + super(*args, &block) + if RightScale::Api::api0_1? + @internal = MultiCloudImageInternal.new(*args, &block) + end + end + end diff --git a/lib/rest_connection/rightscale/multi_cloud_image_cloud_setting_internal.rb b/lib/rest_connection/rightscale/multi_cloud_image_cloud_setting_internal.rb index b818a5b..c7a49e5 100644 --- a/lib/rest_connection/rightscale/multi_cloud_image_cloud_setting_internal.rb +++ b/lib/rest_connection/rightscale/multi_cloud_image_cloud_setting_internal.rb @@ -1,24 +1,37 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ +# +# API 0.1 +# class MultiCloudImageCloudSettingInternal include RightScale::Api::Base extend RightScale::Api::BaseExtend include RightScale::Api::Internal extend RightScale::Api::InternalExtend + deny_methods :index, :show, :update + def resource_plural_name "multi_cloud_image_cloud_settings" end @@ -40,5 +53,5 @@ def self.create(opts) location = connection.post(self.resource_plural_name, self.resource_singular_name.to_sym => opts) newrecord = self.new('href' => location) end - + end diff --git a/lib/rest_connection/rightscale/multi_cloud_image_internal.rb b/lib/rest_connection/rightscale/multi_cloud_image_internal.rb index 2a227dd..989c0db 100644 --- a/lib/rest_connection/rightscale/multi_cloud_image_internal.rb +++ b/lib/rest_connection/rightscale/multi_cloud_image_internal.rb @@ -1,18 +1,29 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ +# +# API 0.1 +# class MultiCloudImageInternal include RightScale::Api::Base extend RightScale::Api::BaseExtend @@ -45,4 +56,28 @@ def clone MultiCloudImage.new(:href => connection.post(t.path + "/clone")) end + def transform_settings + if @params["multi_cloud_image_cloud_settings"] && @params["multi_cloud_image_cloud_settings"].first.is_a?(Hash) + @params["multi_cloud_image_cloud_settings"].map! { |setting| + # Have to reject because API0.1 returns all clouds + next if setting["fingerprint"] || setting["cloud_id"] > 10 + MultiCloudImageCloudSettingInternal.new(setting) + } + @params["multi_cloud_image_cloud_settings"].compact! + end + end + + def initialize(params={}) + @params = params + transform_settings + end + + def settings + transform_settings + @params["multi_cloud_image_cloud_settings"] + end + + def supported_cloud_ids + @params["multi_cloud_image_cloud_settings"].map { |mcics| mcics.cloud_id } + end end diff --git a/lib/rest_connection/rightscale/permission.rb b/lib/rest_connection/rightscale/permission.rb new file mode 100644 index 0000000..d9ef26b --- /dev/null +++ b/lib/rest_connection/rightscale/permission.rb @@ -0,0 +1,41 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# + +# +# Permission Resource requires "admin" role +# + +class Permission + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + + deny_methods :update + + def self.filters + [:user_href] + end +end diff --git a/lib/rest_connection/rightscale/right_script.rb b/lib/rest_connection/rightscale/right_script.rb index 90ca5be..9bc0d05 100644 --- a/lib/rest_connection/rightscale/right_script.rb +++ b/lib/rest_connection/rightscale/right_script.rb @@ -1,29 +1,45 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ -class RightScript +# +# API 1.0 +# +class RightScript include RightScale::Api::Base extend RightScale::Api::BaseExtend + + deny_methods :create, :destroy, :update + + attr_accessor :internal + def self.from_yaml(yaml) scripts = [] x = YAML.load(yaml) x.keys.each do |script| scripts << self.new('href' => "right_scripts/#{script}", 'name' => x[script].ivars['name']) end - scripts + scripts end def self.from_instance_info(file = "/var/spool/ec2/rs_cache/info.yml") @@ -38,7 +54,13 @@ def self.from_instance_info(file = "/var/spool/ec2/rs_cache/info.yml") x.keys.each do |script| scripts << self.new('href' => "right_scripts/#{script}", 'name' => x[script].ivars['name']) end - scripts + scripts end + def initialize(*args, &block) + super(*args, &block) + if RightScale::Api::api0_1? + @internal = RightScriptInternal.new(*args, &block) + end + end end diff --git a/lib/rest_connection/rightscale/right_script_attachment_internal.rb b/lib/rest_connection/rightscale/right_script_attachment_internal.rb new file mode 100644 index 0000000..0915d29 --- /dev/null +++ b/lib/rest_connection/rightscale/right_script_attachment_internal.rb @@ -0,0 +1,103 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +require 'rest-client' +RestClient.log = ENV["REST_CONNECTION_LOG"] || "stdout" + +# +# API 0.1 +# +class RightScriptAttachmentInternal + include RightScale::Api::Base + extend RightScale::Api::BaseExtend + include RightScale::Api::Internal + extend RightScale::Api::InternalExtend + + deny_methods :index, :create, :update + + def resource_plural_name + "right_script_attachments" + end + + def resource_singular_name + "right_script_attachment" + end + + def self.resource_plural_name + "right_script_attachments" + end + + def self.resource_singular_name + "right_script_attachment" + end + + def self.get_s3_upload_params(right_script_href) + url = self.resource_plural_name + "/get_s3_upload_params" + params = {"right_script_href" => right_script_href} + params = {self.resource_singular_name => params} + connection.get(url, params) + end + +=begin + def self.upload(filepath, right_script_href) + hsh = get_s3_upload_params(right_script_href) + params = {} + hsh.keys.each { |k| params[k.gsub(/-/,"_").to_sym] = hsh[k] } + params[:file] = File.new(filepath, 'rb') + req = RestClient::Request.new({ + :method => :post, + :url => hsh["url"], + :payload => params, + :multipart => true, + }) + s = req.payload.to_s + splitter = s.split("\r\n").first + a = s.split(/#{splitter}-?-?\r\n/) + a.push(a.delete(a.detect { |n| n =~ %r{name="file";} })) + new_payload = a.join(splitter + "\r\n") + splitter + "--\r\n" + + uri = URI.parse(hsh["url"]) + net_http = Net::HTTP::Post.new(uri.request_uri) + req.transmit(uri, net_http, new_payload) + # TODO: Precondition Failing + + callback_uri = URI.parse(hsh["success_action_redirect"]) + connection.get(callback_uri.request_uri) + end +=end + + def download + self.reload unless @params["authenticated_s3_url"] + RestClient.get(@params["authenticated_s3_url"]) + end + + def download_to_file(path=Dir.pwd) + data = self.download + File.open(File.join(path, @params["filename"]), 'w') { |f| f.write(data) } + end + + def reload + uri = URI.parse(self.href || "#{resource_plural_name}/#{@params["id"]}") + @params ? @params.merge!(connection.get(uri.path)) : @params = connection.get(uri.path) + end +end diff --git a/lib/rest_connection/rightscale/right_script_internal.rb b/lib/rest_connection/rightscale/right_script_internal.rb index 3270b65..2998e8d 100644 --- a/lib/rest_connection/rightscale/right_script_internal.rb +++ b/lib/rest_connection/rightscale/right_script_internal.rb @@ -1,25 +1,37 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ - -class RightScriptInternal +# +# API 0.1 +# +class RightScriptInternal include RightScale::Api::Base extend RightScale::Api::BaseExtend include RightScale::Api::Internal extend RightScale::Api::InternalExtend + deny_methods :index, :show + def resource_plural_name "right_scripts" end @@ -36,16 +48,38 @@ def self.resource_singular_name "right_script" end + # NOTE: only RightScriptInternal.create() allows you to pass the ["script"] param. + # Need to request that .save() allows update to "script" + # commits a rightscript def commit(message) t = URI.parse(self.href) RightScript.new(:href => connection.post(t.path + "/commit", :commit_message => message)) end - + # clones a RightScript and returns the new RightScript resource that's been created. def clone t = URI.parse(self.href) RightScript.new(:href => connection.post(t.path + "/clone")) end - + + def fetch_right_script_attachments + t = URI.parse(self.href) + @params["attachments"] = [] + connection.get(t.path + "/right_script_attachments").each { |obj| + obj.merge!("right_script_href" => self.href) + @params["attachments"] << RightScriptAttachmentInternal.new(obj) + } + @params["attachments"] + end + + def attachments + @params["attachments"] ||= fetch_right_script_attachments + end + +=begin + def upload_attachment(file) # TODO + filedata = (File.exists?(file) ? IO.read(file) : file) + end +=end end diff --git a/lib/rest_connection/rightscale/rightscale_api_base.rb b/lib/rest_connection/rightscale/rightscale_api_base.rb index 0a3b1d6..1eece05 100644 --- a/lib/rest_connection/rightscale/rightscale_api_base.rb +++ b/lib/rest_connection/rightscale/rightscale_api_base.rb @@ -1,43 +1,160 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ require 'active_support/inflector' + module RightScale module Api - module BaseExtend - def connection() - @@connection ||= RestConnection::Connection.new + + # TODO: move this to McAuditEntry + DATETIME_FMT = "%Y/%m/%d %H:%M:%S +0000" + + # Every supported AWS cloud must be hardcoded here. + # FIXME: Once this list exceeds 10 entries, must also update cloud_id logic elsewhere! + AWS_CLOUDS = [ + {"cloud_id" => 1, "name" => "AWS US-East"}, + {"cloud_id" => 2, "name" => "AWS EU"}, + {"cloud_id" => 3, "name" => "AWS US-West"}, + {"cloud_id" => 4, "name" => "AWS AP-Singapore"}, + {"cloud_id" => 5, "name" => "AWS AP-Tokyo"}, + {"cloud_id" => 6, "name" => "AWS US-Oregon"}, + {"cloud_id" => 7, "name" => "AWS SA-Sao Paulo"}, + {"cloud_id" => 8, "name" => "AWS AP-Sydney"}, + ] + + BASE_COOKIE_REFRESH = proc do + # Refresh cookie by logging in again + def refresh_cookie + # login + @cookie = nil + resp = get("login") + unless resp.code == "302" || resp.code == "204" + raise "ERROR: Login failed. #{resp.message}. Code:#{resp.code}" + end + @cookie = resp.response['set-cookie'] + true + end + end + + # Pass no arguments to reset to the default configuration, + # pass a hash to update the settings for all API versions + def self.update_connection_settings(*settings) + if settings.size > 1 + raise ArgumentError.new("wrong number of arguments (#{settings.size} for 1)") + end + konstants = constants.map { |c| const_get(c) } + konstants.reject! { |c| !(Module === c) } + konstants.reject! { |c| !(c.instance_methods.include?("connection")) } + konstants.each do |c| + c.instance_exec(settings) do |opts| + class_variable_set("@@connection", RestConnection::Connection.new(*opts)) + end + end + @@api0_1, @@api1_0, @@api1_5 = nil, nil, nil + true + end + + # Active API version probing + # TODO: probe passively instead + # TODO: move this logic to a separate file + + # Checks for API 0.1 access + # Requires an account with internal API access on a legacy cluster + def self.api0_1? + if class_variable_defined?("@@api0_1") + return @@api0_1 unless @@api0_1.nil? + end + Ec2SshKeyInternal.find_all + @@api0_1 = true + rescue RestConnection::Errors::Forbidden + @@api0_1 = false + rescue RestConnection::Errors::UnprocessableEntity + @@api0_1 = false + end + + # Checks for API 1.0 access + # Should always succeed + def self.api1_0? + if class_variable_defined?("@@api1_0") + return @@api1_0 unless @@api1_0.nil? + end + Ec2SecurityGroup.find_all + @@api1_0 = true + rescue RestConnection::Errors::Forbidden + @@api1_0 = false + end + + # Check for API 1.5 access + # Should always succeed + def self.api1_5? + if class_variable_defined?("@@api1_5") + return @@api1_5 unless @@api1_5.nil? + end + Cloud.find_all + @@api1_5 = true + rescue RestConnection::Errors::Forbidden + @@api1_5 = false + end + + module BaseConnection + + # Config for API 1.0 + def connection(*opts) + @@connection ||= RestConnection::Connection.new(*opts) settings = @@connection.settings - settings[:common_headers]["X_API_VERSION"] = "1.0" + settings[:common_headers]["X_API_VERSION"] = "1.0" settings[:api_href] = settings[:api_url] settings[:extension] = ".js" + + unless @@connection.respond_to?(:refresh_cookie) + @@connection.instance_exec(&(RightScale::Api::BASE_COOKIE_REFRESH)) + end + + @@connection.refresh_cookie unless @@connection.cookie @@connection end + end + + module BaseExtend + include RightScale::Api::BaseConnection def resource_plural_name self.to_s.underscore.pluralize - end + end def resource_singular_name self.to_s.underscore end + # matches using result of block match expression # ex: Server.find_by(:nickname) { |n| n =~ /production/ } def find_by(attrib, &block) - self.find_all.select do |s| + attrib = attrib.to_sym + if self.filters.include?(attrib) + connection.logger("#{self} includes the filter '#{attrib}', you might be able to speed up this API call") + end + self.find_all.select do |s| yield(s[attrib.to_s]) end end @@ -59,14 +176,15 @@ def find_by_cloud_id(cloud_id) end def find_by_nickname(nickname) - connection.logger("DEPRICATION WARNING: use of find_by_nickname is depricated, please use find_by(:nickname) { |n| n == '#{nickname}' } ") + connection.logger("DEPRECATION WARNING: use of find_by_nickname is deprecated, please use find_by(:nickname) { |n| n == '#{nickname}' } ") self.find_by(:nickname) { |n| n == nickname } end - # the argument can be - # 1) takes href (URI), - # 2) or id (Integer) - # 3) or symbol :all, :first, :last + # Retrieves one or more resources of the same type. + # + # @param [Integer|Symbol|String] href should be one of the following: resource id, :all, :first, :last, resource href + # @param [Hash] additional_params if href is an integer, will be part of retrieve request + # @param [Block] block if href is a symbol, will be used inside select block to refine results def find(href, additional_params={}, &block) if href.is_a?(Integer) return self.new(connection.get(self.resource_plural_name + "/#{href}", additional_params)) @@ -79,7 +197,7 @@ def find(href, additional_params={}, &block) return results elsif href == :first return results.first - elsif href == :last + elsif href == :last return results.last end elsif uri = URI.parse(href) @@ -89,7 +207,7 @@ def find(href, additional_params={}, &block) end def find_by_id(id) - connection.logger("DEPRICATION WARNING: use of find_by_id is depricated, please use find(id) ") + connection.logger("DEPRECATION WARNING: use of find_by_id is deprecated, please use find(id) ") self.find(id) end @@ -100,41 +218,99 @@ def create(opts) newrecord end -# filter is only implemented on some api endpoints + # filter is only implemented on some api endpoints def find_by_nickname_speed(nickname) self.find_with_filter('nickname' => nickname) end -# filter is only implemented on some api endpoints + # filter is only implemented on some api endpoints def find_with_filter(filter = {}) - filter_params = [] - filter.each { |key,val| + filter_params = [] + filter.each { |key,val| + unless self.filters.include?(key.to_sym) + raise ArgumentError.new("#{key} is not a valid filter for resource #{self.resource_singular_name}") + end filter_params << "#{key}=#{val}" - } + } a = Array.new connection.get(self.resource_plural_name, :filter => filter_params).each do |object| a << self.new(object) end return a end + + def filters() + [] + end + + def [](*args) + ret = [] + args.each { |arg| + temp = [] + begin + if arg.is_a?(Hash) + if arg.keys.first.to_s == "cloud_id" + temp << find_by_cloud_id(arg.values.first.to_i) + else + temp << find_with_filter(arg) + end + elsif arg.is_a?(Regexp) + temp << find_by(:nickname) { |n| n =~ arg } + else + temp << find(arg) + end + rescue + end + temp.flatten! + if temp.empty? + all = find_all + if arg.is_a?(Hash) + temp << all.select { |v| v.__send__(arg.keys.first.to_sym) =~ /#{arg.values.first}/ } + elsif arg.is_a?(Regexp) + temp += all.select { |n| n.name =~ arg } + temp += all.select { |n| n.nickname =~ arg } if temp.empty? + else + temp += all.select { |n| n.name =~ /#{arg}/ } + temp += all.select { |n| n.nickname =~ /#{arg}/ } if temp.empty? + end + end + ret += temp + } + return (args.empty? ? find_all : ret.flatten.uniq) + end + + def deny_methods(*symbols) + symbols.map! { |sym| sym.to_sym } + if symbols.delete(:index) + symbols |= [:find_all, :find_by, :find_by_cloud_id, :find_by_nickname, :find_by_nickname_speed, :find_with_filter] + end + if symbols.delete(:show) + symbols |= [:show, :reload, :find, :find_by_id] + end + if symbols.delete(:update) + symbols |= [:save, :update] + end + symbols.each do |sym| + sym = sym.to_sym + eval_str = "undef #{sym.inspect}" + if self.respond_to?(sym) + instance_eval(eval_str) + elsif self.new.respond_to?(sym) + class_eval(eval_str) + end + end + end end module Base + include RightScale::Api::BaseConnection + # The params hash of attributes for direct manipulation attr_accessor :params def initialize(params = {}) @params = params end - def connection() - @@connection ||= RestConnection::Connection.new - settings = @@connection.settings - settings[:common_headers]["X_API_VERSION"] = "1.0" - settings[:api_href] = settings[:api_url] - settings[:extension] = ".js" - @@connection - end - def resource_plural_name self.class.to_s.underscore.pluralize end @@ -168,34 +344,47 @@ def method_missing(method_name, *args) @params[mn] = args[0] @params[mn_dash] = args[0] end - return @params[mn] + return @params[mn] elsif @params[mn_dash] if assignment - @params[mn_dash] = args[0] + @params[mn_dash] = args[0] @params[mn] = args[0] end - return @params[mn_dash] + return @params[mn_dash] elsif @params[mn.to_sym] return @params[mn.to_sym] elsif assignment @params[mn] = args[0] @params[mn_dash] = args[0] - return @params[mn] - else + return @params[mn] + else return nil #raise "called unknown method #{method_name} with #{args.inspect}" end end def [](name) - try_these = [name, name.to_s.gsub(/_/,'-'), name.to_sym] + try_these = [name.to_s, name.to_s.gsub(/_/,'-'), name.to_sym] + try_these.each do |t| + if @params[t] + return @params[t] + end + end + nil + end + + def []=(name,val) + try_these = [name.to_s, name.to_s.gsub(/_/,'-'), name.to_sym] try_these.each do |t| if @params[t] - return @params[name] - else - return nil + @params[t] = val end end + val + end + + def rs_id + self.href.split(/\//).last end end diff --git a/lib/rest_connection/rightscale/rightscale_api_gateway.rb b/lib/rest_connection/rightscale/rightscale_api_gateway.rb index 5a4c529..44e94c7 100644 --- a/lib/rest_connection/rightscale/rightscale_api_gateway.rb +++ b/lib/rest_connection/rightscale/rightscale_api_gateway.rb @@ -1,48 +1,315 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# + module RightScale module Api - module Gateway - def connection - @@gateway_connection ||= RestConnection::Connection.new + + # + # Refresh cookie by logging in again + # + GATEWAY_COOKIE_REFRESH = proc do + def refresh_cookie + # login + ignored, account = @settings[:api_url].split(/\/acct\//) if @settings[:api_url].include?("acct") + params = { + "email" => @settings[:user], + "password" => @settings[:pass], + "account_href" => "/api/accounts/#{account}" + } + @cookie = nil + resp = post("session", params) + unless resp.code == "302" || resp.code == "204" + raise "ERROR: Login failed. #{resp.message}. Code:#{resp.code}" + end + # TODO: handle 302 redirects + @cookie = resp.response['set-cookie'] + + # test session + resp = get("session") + raise "ERROR: Invalid session. #{resp["message"]}." unless resp.is_a?(Hash) + true + end + end + + module GatewayConnection + + # + # Config for API 1.5 + # + def connection(*opts) + @@gateway_connection ||= RestConnection::Connection.new(*opts) settings = @@gateway_connection.settings - settings[:common_headers]["X_API_VERSION"] = "1.5" + settings[:common_headers]["X_API_VERSION"] = "1.5" settings[:api_href], account = settings[:api_url].split(/\/acct\//) if settings[:api_url].include?("acct") - settings[:extension] = "" - unless @@gateway_connection.cookie - # login - params = { "email" => settings[:user], "password" => settings[:pass], "account_href" => "/api/accounts/#{account}" } - resp = @@gateway_connection.post("session", params) - raise "ERROR: Login failed. #{resp.message}. Code:#{resp.code}" unless resp.code == "302" || resp.code == "204" - @@gateway_connection.cookie = resp.response['set-cookie'] - - # test session - resp, data = @@gateway_connection.get("session") - raise "ERROR: Invalid session. #{resp.message}. Code:#{resp.code}" unless resp.code == "200" + settings[:extension] = ".json" + + unless @@gateway_connection.respond_to?(:refresh_cookie) + @@gateway_connection.instance_exec(&(RightScale::Api::GATEWAY_COOKIE_REFRESH)) end + + @@gateway_connection.refresh_cookie unless @@gateway_connection.cookie @@gateway_connection end end + module Gateway + include RightScale::Api::Base + include RightScale::Api::GatewayConnection + + def initialize(params = {}) + @params = parse_params(params) + end + + def parse_params(params = {}) + params + end + + def nickname + raise TypeError.new("@params isn't a Hash! @params.to_s=#{@params.to_s}") unless @params.is_a?(Hash) + @params["nickname"] || @params["name"] + end + + def rediscover + self.reload if @params['href'] + raise "Cannot find attribute 'nickname' or 'name' in #{self.inspect}. Aborting." unless self.nickname + if self.class.filters.include?(:name) + @params = self.class.find_with_filter(:name => self.nickname).first.params + else + @params = self.class.find_by(:name) { |n| n == self.nickname }.first.params + end + end + + def hash_of_links + ret = {} + self.rediscover unless @params['links'] + @params['links'].each { |link| ret[link['rel']] = link['href'] } if @params['links'] + ret + end + + def href + return @params['href'] if @params['href'] + ret = nil + self.rediscover unless @params['links'] + @params['links'].each { |link| ret = link['href'] if link['rel'] == 'self' } + ret + end + + def actions + ret = [] + self.rediscover unless @params['actions'] + @params['actions'].each { |action| ret << action['rel'] } + ret + end + + def save + update + end + + def method_missing(method_name, *args) + puts "DEBUG: method_missing in #{self.class.to_s}: #{method_name.to_s}" if ENV['REST_CONNECT_DEBUG'] + mn = method_name.to_s + assignment = mn.gsub!(/=/,"") + mn_dash = mn.gsub(/_/,"-") + if self[mn] + if assignment + self[mn] = args[0] + self[mn_dash] = args[0] + end + return self[mn] + elsif self[mn_dash] + if assignment + self[mn_dash] = args[0] + self[mn] = args[0] + end + return self[mn_dash] + elsif self[mn.to_sym] + return self[mn.to_sym] + elsif assignment + self[mn] = args[0] + self[mn_dash] = args[0] + return self[mn] + else + return nil + warn "!!!! WARNING - called unknown method #{method_name.to_s}# with #{args.inspect}" + #raise "called unknown method #{method_name.to_s}# with #{args.inspect}" + end + end + + def [](name) + try_these = [name.to_s, name.to_s.gsub(/_/,'-'), name.to_sym] + if try_these.include?(:nickname) + try_these += ["name", :name] + end + try_these.each do |t| + if @params[t] + return @params[t] + elsif hash_of_links[t] + return hash_of_links[t] + end + end + return nil + end + + def []=(name,val) + try_these = [name.to_s, name.to_s.gsub(/_/,'-'), name.to_sym] + if try_these.include?(:nickname) + try_these += ["name", :name] + end + try_these.each do |t| + if @params[t] + @params[t] = val + elsif hash_of_links[t] + @params['links'].each { |link| + link['href'] = val if link['rel'] == t + } + end + end + val + end + + def load(resource) + mod = RightScale::Api::GatewayExtend + @@gateway_resources ||= Object.constants.map do |const| + klass = Object.const_get(const) + (mod === klass ? klass : nil) + end.compact + pp @@gateway_resources + if mod === resource + klass = resource + elsif resource.is_a?(String) or resource.is_a?(Symbol) + klass = @@gateway_resources.detect do |const| + [const.resource_singular_name, const.resource_plural_name].include?(resource.to_s) + end + elsif Class === resource + raise TypeError.new("#{resource} doesn't extend #{mod}") + else + raise TypeError.new("can't convert #{resource.class} into supported Class") + end + + if self[klass.resource_singular_name] + return klass.load(self[klass.resource_singular_name]) + elsif self[klass.resource_plural_name] + return klass.load_all(self[klass.resource_plural_name]) + else + raise NameError.new("no resource_hrefs found for #{klass}") + end + end + end + module GatewayExtend - def connection - @@gateway_connection ||= RestConnection::Connection.new - settings = @@gateway_connection.settings - settings[:common_headers]["X_API_VERSION"] = "1.5" - settings[:api_href], account = settings[:api_url].split(/\/acct\//) if settings[:api_url].include?("acct") - settings[:extension] = "" - unless @@gateway_connection.cookie - # login - params = { "email" => settings[:user], "password" => settings[:pass], "account_href" => "/api/accounts/#{account}" } - resp = @@gateway_connection.post("session", params) - raise "ERROR: Login failed. #{resp.message}. Code:#{resp.code}" unless resp.code == "302" || resp.code == "204" - @@gateway_connection.cookie = resp.response['set-cookie'] - - # test session - resp, data = @@gateway_connection.get("session") - raise "ERROR: Invalid session. #{resp.message}. Code:#{resp.code}" unless resp.code == "200" + include RightScale::Api::BaseExtend + include RightScale::Api::GatewayConnection + + def find_by(attrib, *args, &block) + attrib = attrib.to_sym + attrib = :name if attrib == :nickname + if self.filters.include?(attrib) + connection.logger("#{self} includes the filter '#{attrib}', you might be able to speed up this API call") end - @@gateway_connection + self.find_all(*args).select do |s| + yield(s[attrib.to_s]) + end + end + + def find(*args) + if args.length > 1 + id = args.pop + url = "#{parse_args(*args)}#{self.resource_plural_name}/#{id}" + return self.new(connection.get(url)) + else + return super(*args) + end + end + + def find_all(*args) + a = Array.new + url = "#{parse_args(*args)}#{self.resource_plural_name}" + connection.get(url).each do |object| + a << self.new(object) + end + return a + end + + def find_with_filter(*args) + filter_params = [] + filter = {} + filter = args.pop if args.last.is_a?(Hash) + filter.each { |key,val| + unless self.filters.include?(key.to_sym) + raise ArgumentError.new("#{key} is not a valid filter for resource #{self.resource_singular_name}") + end + filter_params << "#{key}==#{val}" + } + a = Array.new + url = "#{parse_args(*args)}#{self.resource_plural_name}" + connection.get(url, :filter => filter_params).each do |object| + a << self.new(object) + end + return a + end + + def load(url) + return self.new(connection.get(url)) + end + + def load_all(url) + a = Array.new + connection.get(url).each do |object| + a << self.new(object) + end + return a + end + + def parse_args() + nil + end + + def filters() + [] + end + + # Hack for McMultiCloudImageSetting class to fix a API quirk + def resource_post_name + self.resource_singular_name + end + + def create(*args) + if args.last.is_a?(Hash) + opts = args.pop + else + raise ArgumentError.new("create requires the last argument to be a Hash") + end + url = "#{parse_args(*args)}#{self.resource_plural_name}" + location = connection.post(url, self.resource_post_name.to_sym => opts) + newrecord = self.new('links' => [ {'rel' => 'self', 'href' => location } ]) + newrecord.reload + newrecord end end end end - + diff --git a/lib/rest_connection/rightscale/rightscale_api_internal.rb b/lib/rest_connection/rightscale/rightscale_api_internal.rb index 31fc078..6ff8e6a 100644 --- a/lib/rest_connection/rightscale/rightscale_api_internal.rb +++ b/lib/rest_connection/rightscale/rightscale_api_internal.rb @@ -1,26 +1,60 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 0.1 +# module RightScale module Api - module Internal - def connection - @@little_brother_connection ||= RestConnection::Connection.new + module InternalConnection + + # + # Config for API 0.1 + # Only works for legacy clusters + # + def connection(*opts) + @@little_brother_connection ||= RestConnection::Connection.new(*opts) settings = @@little_brother_connection.settings - settings[:common_headers]["X_API_VERSION"] = "0.1" + settings[:common_headers]["X_API_VERSION"] = "1.0" settings[:api_href] = settings[:api_url] settings[:extension] = ".js" + + unless @@little_brother_connection.respond_to?(:refresh_cookie) + @@little_brother_connection.instance_exec(&(RightScale::Api::BASE_COOKIE_REFRESH)) + end + + @@little_brother_connection.refresh_cookie unless @@little_brother_connection.cookie + settings[:common_headers]["X_API_VERSION"] = "0.1" @@little_brother_connection end end + module Internal + include RightScale::Api::InternalConnection + end + module InternalExtend - def connection - @@little_brother_connection ||= RestConnection::Connection.new - settings = @@little_brother_connection.settings - settings[:common_headers]["X_API_VERSION"] = "0.1" - settings[:api_href] = settings[:api_url] - settings[:extension] = ".js" - @@little_brother_connection - end + include RightScale::Api::InternalConnection end end end - diff --git a/lib/rest_connection/rightscale/rightscale_api_mc_input.rb b/lib/rest_connection/rightscale/rightscale_api_mc_input.rb new file mode 100644 index 0000000..955b249 --- /dev/null +++ b/lib/rest_connection/rightscale/rightscale_api_mc_input.rb @@ -0,0 +1,40 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +module RightScale + module Api + module McInput + def get_inputs + self["inputs"] = connection.get(self.href + "/inputs") + end + + def show + inst_href = URI.parse(self.href) + @params.merge! connection.get(inst_href.path, 'view' => "inputs") + end + end + end +end diff --git a/lib/rest_connection/rightscale/rightscale_api_mc_taggable.rb b/lib/rest_connection/rightscale/rightscale_api_mc_taggable.rb new file mode 100644 index 0000000..8d18a65 --- /dev/null +++ b/lib/rest_connection/rightscale/rightscale_api_mc_taggable.rb @@ -0,0 +1,65 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +module RightScale + module Api + module McTaggable + include RightScale::Api::Taggable + def add_tags(*args) + return false if args.empty? + McTag.set(self.href, args.uniq) + self.tags(true) + end + + def remove_tags(*args) + return false if args.empty? + McTag.unset(self.href, args.uniq) + @params["tags"] -= args + self.tags(true) + end + + def tags(reload=false) + @params["tags"] ||= [] + @params["tags"].map! { |item| item.is_a?(Hash) ? item["name"] : item } + @params["tags"].deep_merge!(McTag.search_by_href(self.href).first["tags"].map { |hsh| hsh["name"] }) if reload or @params["tags"].empty? + @params["tags"] + end + end + + module McTaggableExtend + def find_by_tags(*args) + a = Array.new + search = McTag.search(self.resource_plural_name, args.uniq).first + if search + search["links"].each do |hash| + a << self.find(hash["href"]) + end + end + return a + end + end + end +end diff --git a/lib/rest_connection/rightscale/rightscale_api_resources.rb b/lib/rest_connection/rightscale/rightscale_api_resources.rb index 3f7c75a..5c5e6dd 100644 --- a/lib/rest_connection/rightscale/rightscale_api_resources.rb +++ b/lib/rest_connection/rightscale/rightscale_api_resources.rb @@ -1,45 +1,94 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . - +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ require 'rest_connection/rightscale/rightscale_api_base' require 'rest_connection/rightscale/rightscale_api_internal' require 'rest_connection/rightscale/rightscale_api_gateway' +require 'rest_connection/rightscale/rightscale_api_taggable' +require 'rest_connection/rightscale/rightscale_api_mc_taggable' +require 'rest_connection/rightscale/rightscale_api_mc_input' +require 'rest_connection/ssh_hax' +require 'rest_connection/rightscale/alert_spec_subject' +require 'rest_connection/rightscale/server_ec2_ebs_volume' +require 'rest_connection/rightscale/sqs_queue' require 'rest_connection/rightscale/executable' +require 'rest_connection/rightscale/cloud_account' +require 'rest_connection/rightscale/server_internal' require 'rest_connection/rightscale/server' require 'rest_connection/rightscale/deployment' require 'rest_connection/rightscale/status' +require 'rest_connection/rightscale/server_template_internal' require 'rest_connection/rightscale/server_template' -require 'rest_connection/rightscale/right_script' require 'rest_connection/rightscale/instance' require 'rest_connection/rightscale/ec2_security_group' +require 'rest_connection/rightscale/vpc_dhcp_option' +require 'rest_connection/rightscale/ec2_ssh_key_internal' require 'rest_connection/rightscale/ec2_ssh_key' -require 'rest_connection/rightscale/multi_cloud_image' require 'rest_connection/rightscale/tag' +require 'rest_connection/rightscale/mc_tag' +require 'rest_connection/rightscale/task' +require 'rest_connection/rightscale/backup' +require 'rest_connection/rightscale/mc_audit_entry' require 'rest_connection/rightscale/rs_internal' require 'rest_connection/rightscale/audit_entry' require 'rest_connection/rightscale/alert_spec' require 'rest_connection/rightscale/ec2_ebs_volume' require 'rest_connection/rightscale/ec2_ebs_snapshot' -require 'rest_connection/rightscale/server_internal' +require 'rest_connection/rightscale/mc_volume_attachment' +require 'rest_connection/rightscale/mc_volume' +require 'rest_connection/rightscale/mc_volume_snapshot' +require 'rest_connection/rightscale/mc_volume_type' require 'rest_connection/rightscale/mc_server' -require 'rest_connection/rightscale/ec2_ssh_key_internal' -require 'rest_connection/rightscale/server_template_internal' +require 'rest_connection/rightscale/server_interface' +require 'rest_connection/rightscale/mc_instance' +require 'rest_connection/rightscale/monitoring_metric' +require 'rest_connection/rightscale/session' +require 'rest_connection/rightscale/mc_multi_cloud_image_setting' +require 'rest_connection/rightscale/mc_multi_cloud_image' +require 'rest_connection/rightscale/mc_server_template_multi_cloud_image' +require 'rest_connection/rightscale/mc_server_template' +require 'rest_connection/rightscale/right_script_attachment_internal' require 'rest_connection/rightscale/right_script_internal' -require 'rest_connection/rightscale/multi_cloud_image_internal' +require 'rest_connection/rightscale/right_script' require 'rest_connection/rightscale/multi_cloud_image_cloud_setting_internal' +require 'rest_connection/rightscale/multi_cloud_image_internal' +require 'rest_connection/rightscale/multi_cloud_image' require 'rest_connection/rightscale/ec2_server_array' +require 'rest_connection/rightscale/mc_server_array' +require 'rest_connection/rightscale/security_group_rule' +require 'rest_connection/rightscale/mc_security_group' +require 'rest_connection/rightscale/mc_deployment' +require 'rest_connection/rightscale/mc_datacenter' +require 'rest_connection/rightscale/mc_ssh_key' require 'rest_connection/rightscale/ec2_elastic_ip' require 'rest_connection/rightscale/credential' +require 'rest_connection/rightscale/cloud' +require 'rest_connection/rightscale/instance_type' +require 'rest_connection/rightscale/mc_instance_type' +require 'rest_connection/rightscale/mc_image' +require 'rest_connection/rightscale/macro' +require 'rest_connection/rightscale/s3_bucket' +require 'rest_connection/rightscale/account' +require 'rest_connection/rightscale/child_account' +require 'rest_connection/rightscale/permission' +require 'rest_connection/rightscale/user' diff --git a/lib/rest_connection/rightscale/rightscale_api_taggable.rb b/lib/rest_connection/rightscale/rightscale_api_taggable.rb new file mode 100644 index 0000000..97db92a --- /dev/null +++ b/lib/rest_connection/rightscale/rightscale_api_taggable.rb @@ -0,0 +1,124 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +module RightScale + module Api + module Taggable + def add_tags(*args) + return false if args.empty? + Tag.set(self.href, args.uniq) + self.tags(true) + end + + def remove_tags(*args) + return false if args.empty? + Tag.unset(self.href, args.uniq) + @params["tags"] -= args + self.tags(true) + end + + def tags(reload=false) + @params["tags"] ||= [] + @params["tags"].map! { |item| item.is_a?(Hash) ? item["name"] : item } + @params["tags"].deep_merge!(Tag.search_by_href(self.href).map { |hsh| hsh["name"] }) if reload or @params["tags"].empty? + @params["tags"] + end + + def remove_info_tags(*tag_keys) + remove_tags_by_namespace("info", *tag_keys) + end + + def set_info_tags(hsh={}) + set_tags_by_namespace("info", hsh) + end + + def get_info_tags(*tag_keys) + tags = get_tags_by_namespace("info") + tags.each { |resource,hsh| + hsh.reject! { |key,value| + rej = false + rej = !tag_keys.include?(key) unless tag_keys.empty? + rej + } + } + return tags + end + + def remove_tags_by_namespace(namespace, *tag_keys) + tags_to_unset = [] + tags = get_tags_by_namespace(namespace) + tags.each { |res,hsh| + hsh.each { |k,v| + tags_to_unset << "#{namespace}:#{k}=#{v}" if tag_keys.include?(k) + } + } + self.remove_tags(*tags_to_unset) + end + + def set_tags_by_namespace(namespace, hsh={}) + keys_to_change = [] + tags_to_set = [] + hsh.each { |k,v| keys_to_change << k; tags_to_set << "#{namespace}:#{k}=#{v}" } + self.remove_tags_by_namespace(namespace, *keys_to_change) + self.add_tags(*tags_to_set) + end + + def get_tags_by_namespace(namespace) + ret = {} + tags = {"self" => self.tags(true)} + tags.each { |res,ary| + ret[res] ||= {} + ary.each { |tag| + next unless tag.start_with?("#{namespace}:") + key = tag.split("=").first.split(":")[1..-1].join(":") + value = tag.split(":")[1..-1].join(":").split("=")[1..-1].join("=") + ret[res][key] = value + } + } + return ret + end + + def set_tags_to(*args) + STDERR.puts "set_tags_to(...) is deprecated" + self.clear_tags("info") + self.add_tags(*(args.uniq)) + end + + def clear_tags(namespace = nil) + tag_ary = self.tags(true) + tag_ary = tag_ary.select { |tag| tag.start_with?("#{namespace}:") } if namespace + self.remove_tags(*tag_ary) + end + end + + module TaggableExtend + def find_by_tags(*args) + a = Array.new + Tag.search(self.resource_singular_name, args.uniq).each do |object| + a << self.new(object) + end + return a + end + end + end +end diff --git a/lib/rest_connection/rightscale/rs_internal.rb b/lib/rest_connection/rightscale/rs_internal.rb index a3dbae3..50088b5 100644 --- a/lib/rest_connection/rightscale/rs_internal.rb +++ b/lib/rest_connection/rightscale/rs_internal.rb @@ -1,41 +1,41 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ -# -# You must have special API access to use these internal API calls. -# -class RsInternal +# +# API 0.1 +# +class RsInternal include RightScale::Api::Base extend RightScale::Api::BaseExtend + extend ::RightScale::Api::InternalConnection def connection - @@little_brother_connection ||= RestConnection::Connection.new - settings = @@little_brother_connection.settings - settings[:common_headers]["X_API_VERSION"] = "0.1" - settings[:api_href] = settings[:api_url] - settings[:extension] = ".js" - @@little_brother_connection + self.connection end def self.connection - @@little_brother_connection ||= RestConnection::Connection.new - settings = @@little_brother_connection.settings - settings[:common_headers]["X_API_VERSION"] = "0.1" - settings[:api_href] = settings[:api_url] - settings[:extension] = ".js" - @@little_brother_connection + # call connection defined in InternalConnection module + super end def self.get_server_template_multi_cloud_images(server_template_href) diff --git a/lib/rest_connection/rightscale/s3_bucket.rb b/lib/rest_connection/rightscale/s3_bucket.rb new file mode 100644 index 0000000..75b9893 --- /dev/null +++ b/lib/rest_connection/rightscale/s3_bucket.rb @@ -0,0 +1,48 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.0 +# +class S3Bucket + include RightScale::Api::Base + extend RightScale::Api::BaseExtend + + deny_methods :update + + def self.resource_singular_name + "s3_bucket" + end + + def self.resource_plural_name + "s3_buckets" + end + + def resource_singular_name + "s3_bucket" + end + + def resource_plural_name + "s3_buckets" + end +end diff --git a/lib/rest_connection/rightscale/security_group_rule.rb b/lib/rest_connection/rightscale/security_group_rule.rb new file mode 100644 index 0000000..cd57a0e --- /dev/null +++ b/lib/rest_connection/rightscale/security_group_rule.rb @@ -0,0 +1,39 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +class SecurityGroupRule + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + + deny_methods :update + + def self.parse_args(cloud_id=nil, security_group_id=nil) + if cloud_id.nil? ^ security_group_id.nil? + raise ArgumentError.new("#{self} requires either 0 arguments, or 2 arguments") + end + "clouds/#{cloud_id}/security_groups/#{security_group_id}/" + end +end diff --git a/lib/rest_connection/rightscale/server.rb b/lib/rest_connection/rightscale/server.rb index a12b164..1658ac7 100644 --- a/lib/rest_connection/rightscale/server.rb +++ b/lib/rest_connection/rightscale/server.rb @@ -1,37 +1,71 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ -require 'rest_connection/ssh_hax' - -class Server +# +# API 1.0 +# +class Server include RightScale::Api::Base extend RightScale::Api::BaseExtend include SshHax + include RightScale::Api::Taggable + extend RightScale::Api::TaggableExtend + + attr_accessor :internal + + def self.filters + [ + :aws_id, + :created_at, + :deployment_href, + :ip_address, + :nickname, + :private_ip_address, + :updated_at + ] + end def self.create(opts) create_options = Hash.new create_options[self.resource_singular_name.to_sym] = opts create_options["cloud_id"] = opts[:cloud_id] if opts[:cloud_id] - location = connection.post(self.resource_plural_name,create_options) + create_options[self.resource_singular_name.to_sym][:mci_href] = nil + create_options[self.resource_singular_name.to_sym][:inputs] = nil + location = connection.post(self.resource_plural_name,create_options) newrecord = self.new('href' => location) newrecord.reload newrecord.parameters #transform the parameters! newrecord end - # The RightScale api returns the server parameters as a hash with "name" and "value". + def initialize(*args, &block) + super(*args, &block) + if RightScale::Api::api0_1? + @internal = ServerInternal.new(*args, &block) + end + end + + # The RightScale api returns the server parameters as a hash with "name" and "value". # This must be transformed into a hash in case we want to PUT this back to the API. def transform_parameters(parameters) new_params_hash = {} @@ -48,12 +82,13 @@ def parameters if @params['parameters'].is_a?(Array) @params['parameters'] = transform_parameters(@params['parameters']) end - @params['parameters'] + @params['parameters'] ||= {} end # This is overriding the default save with one that can massage the parameters def save self.parameters #transform the parameters, if they're not already!! + self.tags #transform the tags, if they're not already!! uri = URI.parse(self.href) connection.put(uri.path, resource_singular_name.to_sym => @params) end @@ -64,36 +99,58 @@ def save def wait_for_state(st,timeout=1200) reload connection.logger("#{nickname} is #{self.state}") + step = 15 + catch_early_terminated = 1200 / step while(timeout > 0) return true if state =~ /#{st}/ + return true if state =~ /terminated|stopped/ && st =~ /terminated|stopped/ raise "FATAL error, this server is stranded and needs to be #{st}: #{nickname}, see audit: #{self.audit_link}" if state.include?('stranded') && !st.include?('stranded') - sleep 30 - timeout -= 30 + raise "FATAL error, this server went to error state and needs to be #{st}: #{nickname}, see audit: #{self.audit_link}" if state.include?('error') && st !~ /error|terminated|stopped/ connection.logger("waiting for server #{nickname} to go #{st}, state is #{state}") + if state =~ /terminated|stopped|inactive|error/ and st !~ /terminated|stopped|inactive|error/ + if catch_early_terminated <= 0 + raise "FATAL error, this server entered #{state} state waiting for #{st}: #{nickname}" + end + catch_early_terminated -= 1 + end + sleep step + timeout -= step reload end raise "FATAL, this server #{self.audit_link} timed out waiting for the state to be #{st}" if timeout <= 0 end # waits until the server is operational and dns_name is available - def wait_for_operational_with_dns - timeout = 600 - wait_for_state("operational") - while(timeout > 0) + def wait_for_operational_with_dns(operational_timeout_in_seconds = 1200, dns_timeout_in_seconds = operational_timeout_in_seconds / 2) + # First, wait for the server to go operational + wait_for_state("operational", operational_timeout_in_seconds) + + # Log that we are switching to waiting for dns + connection.logger "#{self.nickname} is operational, now checking for dns name/ip address..." + + # Now poll for valid dns + dns_timeout = dns_timeout_in_seconds + dns_step = 15 + while(dns_timeout > 0) self.settings - break if self['dns-name'] && !self['dns-name'].empty? && self['private-dns-name'] && !self['private-dns-name'].empty? - connection.logger "waiting for dns-name for #{self.nickname}" - sleep 30 - timeout -= 30 + if self.reachable_ip + # Got a dns name/ip address so log that and return (note that "reachable_ip" returns a dns name on AWS and an ip address on generic clouds) + connection.logger "Got dns name/ip address: #{self.reachable_ip}." + return + end + connection.logger "Waiting #{dns_step} seconds before checking for dns name/ip address on #{self.nickname}..." + sleep dns_step + dns_timeout -= dns_step end - connection.logger "got DNS: #{self['dns-name']}" - raise "FATAL, this server #{self.audit_link} timed out waiting for DNS" if timeout <= 0 + + raise "FATAL, server #{self.nickname}, #{self.audit_link} timed out waiting for dns name/ip address, waited for #{dns_timeout_in_seconds}." end def audit_link # proof of concept for now - server_id = self.href.split(/\//).last - audit_href = "https://my.rightscale.com/servers/#{server_id}#audit_entries" +# server_id = self.href.split(/\//).last +# audit_href = "https://my.rightscale.com/servers/#{server_id}#auditentries" + audit_href = self.href.gsub(/api\//,"") + "#auditentries" "#{audit_href}" end @@ -116,20 +173,42 @@ def stop end end + def force_stop + if self.current_instance_href + t = URI.parse(self.href) + connection.post(t.path + '/stop') + connection.post(t.path + '/stop') + else + connection.logger("WARNING: was in #{self.state} and had a current_instance_href so skiping stop call") + end + end + # Uses ServerInternal api to start and stop EBS based instances def start_ebs - @server_internal = ServerInternal.new(:href => self.href) - @server_internal.start +# @server_internal = ServerInternal.new(:href => self.href) +# @server_internal.start + if self.state == "stopped" + t = URI.parse(self.href) + return connection.post(t.path + '/start_ebs') + else + connection.logger("WARNING: was in #{self.state} so skiping start_ebs call") + end end def stop_ebs - @server_internal = ServerInternal.new(:href => self.href) - @server_internal.stop +# @server_internal = ServerInternal.new(:href => self.href) +# @server_internal.stop + if self.current_instance_href + t = URI.parse(self.href) + connection.post(t.path + '/stop_ebs') + else + connection.logger("WARNING: was in #{self.state} and had a current_instance_href so skiping stop_ebs call") + end end # Works on v4 and v5 images. # *executable can be an <~Executable> or <~RightScript> - def run_executable(executable, opts=nil) + def run_executable(executable, opts=nil, ignore_lock=false) script_options = Hash.new script_options[:server] = Hash.new if executable.is_a?(Executable) @@ -143,7 +222,7 @@ def run_executable(executable, opts=nil) else raise "Invalid class passed to run_executable, needs Executable or RightScript, was:#{executable.class}" end - + if not opts.nil? and opts.has_key?(:ignore_lock) script_options[:server][:ignore_lock] = "true" opts.delete(:ignore_lock) @@ -151,6 +230,7 @@ def run_executable(executable, opts=nil) serv_href = URI.parse(self.href) script_options[:server][:parameters] = opts unless opts.nil? + script_options[:server][:ignore_lock] = "true" if ignore_lock location = connection.post(serv_href.path + '/run_executable', script_options) AuditEntry.new('href' => location) end @@ -167,16 +247,32 @@ def run_script(script,opts=nil) script_options[:server][:parameters] = opts unless opts.nil? location = connection.post(serv_href.path + '/run_script', script_options) Status.new('href' => location) - end + end def set_input(name, value) serv_href = URI.parse(self.href) connection.put(serv_href.path, :server => {:parameters => {name.to_sym => value} }) end - def set_inputs(hash) + def set_inputs(hash = {}) + set_current_inputs(hash) + set_next_inputs(hash) + end + + def set_current_inputs(hash = {}) + if self.current_instance_href and self.state != "stopped" + self.reload_as_current + serv_href = URI.parse(self.href) + connection.put(serv_href.path, :server => {:parameters => hash}) + settings + self.reload_as_next + end + end + + def set_next_inputs(hash = {}) serv_href = URI.parse(self.href) connection.put(serv_href.path, :server => {:parameters => hash}) + settings end def set_template(href) @@ -191,7 +287,7 @@ def settings def attach_volume(params) hash = {} - hash[:server] = params + hash[:server] = params serv_href = URI.parse(self.href) connection.post(serv_href.path + "/attach_volume", hash) end @@ -207,24 +303,30 @@ def monitoring end # takes Bool argument to wait for state change (insurance that we can detect a reboot happened) - def reboot(wait_for_state = false) + # *timeout <~Integer> optional, how long to wait for the stopped state before declare failure (in seconds). + def reboot(wait_for_state = false, timeout = 60*7) reload old_state = self.state serv_href = URI.parse(self.href) - connection.post(serv_href.path + "/reboot") + connection.post(serv_href.path + "/reboot") if wait_for_state - wait_for_state_change(old_state) + wait_for_state_change(old_state, timeout) end end - def relaunch + def alert_specs + # TODO + end + + # *timeout <~Integer> optional, how long to wait for the stopped state before declare failure (in seconds). + def relaunch(timeout=1200) self.stop - self.wait_for_state("stopped") - self.start + self.wait_for_state("stopped", timeout) + self.start end - def wait_for_state_change(old_state = nil) - timeout = 60*7 + # *timeout <~Integer> optional, how long to wait for the stopped state before declare failure (in seconds). + def wait_for_state_change(old_state = nil, timeout = 60*7) timer = 0 while(timer < timeout) reload @@ -238,23 +340,153 @@ def wait_for_state_change(old_state = nil) raise("FATAL: timeout after #{timeout}s waiting for state change") end - # Save the servers parameters to the current server (instead of the next server) - def save_current - uri = URI.parse(self.href) - connection.put(uri.path + "/current", resource_singular_name.to_sym => @params) + # Reload the server's basic information from the current server instance + def reload_as_current + uri = URI.parse(self.href + "/current") + @params.merge! connection.get(uri.path) end - # Load server's settings from the current server (instead of the next server) - def settings_current - serv_href = URI.parse(self.href) - @params.merge! connection.get(serv_href.path + "/current" + "/settings") + # Reload the server's basic information from the next server instance + def reload_as_next + uri = URI.parse(self.href.gsub(/\/current/,"")) + @params.merge! connection.get(uri.path) end - # Reload the server's basic information from the current server. - def reload_current - uri = URI.parse(self.href) - @params ? @params.merge!(connection.get(uri.path + "/current")) : @params = connection.get(uri.path) + # Complex logic for determining the cloud_id of even a stopped server + def cloud_id + self.settings + if self.state == "operational" + return self["cloud_id"] + end + cloud_ids = RightScale::Api::AWS_CLOUDS.map { |hsh| hsh["cloud_id"] } + + # Try ssh keys + if self.ec2_ssh_key_href and RightScale::Api::api0_1? + ref = self.ec2_ssh_key_href + cloud_ids.each { |cloud| + if Ec2SshKeyInternal.find_by_cloud_id(cloud.to_s).select { |o| o.href == ref }.first + return cloud + end + } + end + + # Try security groups + if self.ec2_security_groups_href + self.ec2_security_groups_href.each { |sg| + cloud_ids.each { |cloud| + if Ec2SecurityGroup.find_by_cloud_id(cloud.to_s).select { |o| o.href == sg }.first + return cloud + end + } + } + end + + raise "Could not determine cloud_id...try setting an ssh key or security group" end -end + def run_executable_and_wait_for_completed(executable, opts=nil) + run_executable(executable, opts).wait_for_completed + end + + def dns_name + self.settings unless self["dns_name"] + if self.current_instance_href + self["dns_name"] ||= get_tags_by_namespace("server")["current_instance"]["public_ip_0"] + end + self["dns_name"] + end + + def private_ip + self.settings unless @params["private-ip-address"] + if self.current_instance_href + self["private-ip-address"] ||= get_tags_by_namespace("server")["current_instance"]["private_ip_0"] + end + @params["private-ip-address"] + end + + def reachable_ip + if ret = self.dns_name + return ret + elsif ret = self.private_ip + return ret + end + nil + end + + # Override Taggable mixin so that it sets tags on both next and current instances + def tags(*args) + self.reload_as_next if self.href =~ /current/ + super(*args) + end + + def current_tags(reload=true) + self.reload_as_next if self.href =~ /current/ + ret = [] + if self.current_instance_href + ret = Tag.search_by_href(self.current_instance_href).map { |h| h["name"] } + end + ret + end + + def add_tags(*args) + self.reload_as_next if self.href =~ /current/ + return false if args.empty? + args.uniq! + Tag.set(self.href, args) + Tag.set(self.current_instance_href, args) if self.current_instance_href + self.tags(true) + end + + def remove_tags(*args) + self.reload_as_next if self.href =~ /current/ + return false if args.empty? + args.uniq! + Tag.unset(self.href, args) + Tag.unset(self.current_instance_href, args) if self.current_instance_href + self.tags(true) + end + + def get_tags_by_namespace(namespace) + ret = {} + tags = {"self" => self.tags(true)} + tags["current_instance"] = self.current_tags if self.current_instance_href + tags.each { |res,ary| + ret[res] ||= {} + ary.each { |tag| + next unless tag.start_with?("#{namespace}:") + key = tag.split("=").first.split(":")[1..-1].join(":") + value = tag.split(":")[1..-1].join(":").split("=")[1..-1].join("=") + ret[res][key] = value + } + } + return ret + end + def clear_tags(namespace = nil) + tags = self.tags(true) + tags.deep_merge! self.current_tags if self.current_instance_href + tags = tags.select { |tag| tag.start_with?("#{namespace}:") } if namespace + self.remove_tags(*tags) + end + + def lock + unless self.settings['locked'] + serv_href = URI.parse(self.href) + res = connection.put(serv_href.path, :server => {:lock => 'true'}) + res.is_a?(Net::HTTPSuccess) ? true : false + else + connection.logger("Server is already locked") + end + end + + def unlock + if self.settings['locked'] + serv_href = URI.parse(self.href) + res = connection.put(serv_href.path, :server => {:lock => 'false'}) + res.is_a?(Net::HTTPSuccess) ? true : false + else + connection.logger("Server is already unlocked") + end + end + +end diff --git a/lib/rest_connection/rightscale/server_ec2_ebs_volume.rb b/lib/rest_connection/rightscale/server_ec2_ebs_volume.rb new file mode 100644 index 0000000..26fa606 --- /dev/null +++ b/lib/rest_connection/rightscale/server_ec2_ebs_volume.rb @@ -0,0 +1,50 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.0 +# +class ServerEc2EbsVolume + include RightScale::Api::Base + extend RightScale::Api::BaseExtend + include RightScale::Api::Taggable + extend RightScale::Api::TaggableExtend + + deny_methods :index, :update + + def resource_plural_name + "component_ec2_ebs_volumes" + end + + def resource_singular_name + "component_ec2_ebs_volume" + end + + def self.resource_plural_name + "component_ec2_ebs_volumes" + end + + def self.resource_singular_name + "component_ec2_ebs_volume" + end +end diff --git a/lib/rest_connection/rightscale/server_interface.rb b/lib/rest_connection/rightscale/server_interface.rb new file mode 100644 index 0000000..fe0f311 --- /dev/null +++ b/lib/rest_connection/rightscale/server_interface.rb @@ -0,0 +1,272 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +require 'rest_connection/ssh_hax' + +class ServerInterface + attr_reader :multicloud + + def initialize(cid = nil, params = {}, deployment_id = nil) + if cid + @multicloud = (cid.to_i > 10 ? true : false) + elsif params["href"] + @multicloud = false + else + @multicloud = true + end + if @multicloud + if deployment_id + name = params["nickname"] || params["name"] || params[:nickname] || params[:name] + @impl = McServer.find_by(:name, deployment_id) { |n| n == name }.first + else + @impl = McServer.new(params) + end + else + @impl = Server.new(params) + end + self + end + + def create(opts) + location = connection.post(resource_plural_name, translate_create_opts(opts)) + @impl = (@multicloud ? McServer.new('href' => location) : Server.new('href' => location)) + settings + self + end + + def name + nickname + end + + def inspect + @impl.inspect + end + + def self.[](*args) + begin + ret = Server[*args] + raise "" if ret.empty? + rescue + ret = McServer[*args] + end + return ret + end + + def nickname + return @impl.nickname unless @multicloud + return @impl.name if @multicloud + end + + def method_missing(method_name, *args, &block) + @impl.__send__(method_name, *args, &block) + end + + def clean_and_translate_server_params(it) + it.each do |k, v| + clean_and_translate_server_params(v) if v.is_a?(Hash) + end + it.reject! { |k, v| v == nil or v == "" } + it.each { |k, v| it[k] = translate_href(v) if k.to_s =~ /href/ } + it + end + + def translate_create_opts(old_opts, instance_only=false) + fields = [{"1.0" => [:server_template_href], "1.5" => [:server_template_href]}, + {"1.0" => [:cloud_id], "fn" => :map_cloud_id, "1.5" => [:cloud_href]}, + {"1.0" => [:ec2_image_href], "1.5" => [:image_href]}, + {"1.0" => [:ec2_user_data], "1.5" => [:user_data]}, + {"1.0" => [:instance_type], "fn" => :map_instance, "1.5" => [:instance_type_href]}, + {"1.0" => [:ec2_security_groups_href], "1.5" => [:security_group_hrefs]}, + {"1.0" => [:ec2_ssh_key_href], "1.5" => [:ssh_key_href]}, + {"1.0" => [:vpc_subnet_href]}, + {"1.0" => [:ec2_availability_zone]}, + {"1.0" => [:pricing]}, + {"1.0" => [:max_spot_price]}, + { "1.5" => [:inputs]}, + { "1.5" => [:mci_href, :multi_cloud_image_href]}, + { "1.5" => [:datacenter_href]}, + {"1.0" => [:aki_image_href], "1.5" => [:kernel_image_href]}, + {"1.0" => [:ari_image_href], "1.5" => [:ramdisk_image_href]}] + + opts = old_opts.dup + if @multicloud + to = "1.5" + if instance_only + ret = {"instance" => {}} + server = ret["instance"] + else + ret = {"server" => {"instance" => {}}} + ret["server"]["name"] = (opts["name"] ? opts["name"] : opts["nickname"]) + ret["server"]["description"] = opts["description"] + ret["server"]["deployment_href"] = opts["deployment_href"] + server = ret["server"]["instance"] + end + else + to = "1.0" + server = {"nickname" => (opts["nickname"] ? opts["nickname"] : opts["name"])} + server["deployment_href"] = opts["deployment_href"] + ret = {"server" => server} + begin + ret["cloud_id"] = opts["cloud_href"].split(/\/clouds\//).last + rescue Exception => e + ret["cloud_id"] = opts["cloud_id"] + end + end + + fields.each { |hsh| + next unless hsh[to] + hsh[to].each { |field| + vals = opts.select {|k,v| [[hsh["1.0"]] + [hsh["1.5"]]].flatten.include?(k.to_sym) } + vals.flatten! + vals.compact! + if hsh["fn"] + server[field.to_s] = __send__(hsh["fn"], to, opts[vals.first]) unless vals.first.nil? + else + server[field.to_s] = opts[vals.first] unless vals.first.nil? + end + server.delete("inputs") if server["inputs"] && server["inputs"].empty? # Inputs cannot be empty for 1.5 + } + } + clean_and_translate_server_params(ret) + return ret + end + + def map_cloud_id(to, val) + if val.is_a?(String) + begin + val = val.split(/\//).last + rescue Exception => e + end + end + if to == "1.5" + return "https://my.rightscale.com/api/clouds/#{val}" + elsif to == "1.0" + return "#{val}" + end + end + + def map_instance(to, val) + if to == "1.0" + return val + end + val + end + + def translate_href(old_href) + if old_href.is_a?(Array) + new_array = [] + old_href.each { |url| new_array << translate_href(url) } + return new_array + else + href = old_href.dup + if @multicloud + href.gsub!(/ec2_/,'') + href.gsub!(/\/acct\/[0-9]*/,'') + end + return href + end +# if href.include?("acct") +# my_base_href, @account = href.split(/\/acct\//) +# @account, *remaining = @account.split(/\//) +# if @multicloud +# return my_base_href + "/" + remaining.join("/").gsub(/ec2_/,'') +# else +# return href +# end +# else #API 1.5 +# end + end + + # Since RightScale hands back the parameters with a "name" and "value" tags we should + # transform them into the proper hash. This it the same for setting and getting. + def parameters + return @impl.parameters unless @multicloud + return @impl.inputs if @multicloud + end + + def inputs + parameters + end + + def start + launch + end + + def stop + terminate + end + + def launch + return @impl.launch if @multicloud + return @impl.start unless @multicloud + end + + def terminate + return @impl.terminate if @multicloud + return @impl.stop unless @multicloud + end + + def start_ebs + return connection.logger("WARNING: Gateway Servers do not support start_ebs. Ignoring.") if @multicloud + return @impl.start_ebs unless @multicloud + end + + def stop_ebs + return connection.logger("WARNING: Gateway Servers do not support stop_ebs. Ignoring.") if @multicloud + return @impl.stop_ebs unless @multicloud + end + + # This should be used with v4 images only. + def run_script(script,opts=nil) + return connection.logger("WARNING: Gateway Servers do not support run_script. Did you mean run_executable?") if @multicloud + return @impl.run_script(script,opts) unless @multicloud + end + + def attach_volume(params) + return connection.logger("WARNING: Gateway Servers do not support attach_volume. Ignoring.") if @multicloud + return @impl.attach_volume(params) unless @multicloud + end + + def wait_for_state(st,timeout=1200) + if @multicloud and st == "stopped" + st = "inactive" + end + @impl.wait_for_state(st,timeout) + end + + def save(new_params = nil) + if new_params + @impl.settings + if @multicloud + @impl.next_instance.params.merge!(translate_create_opts(new_params, :instance_only)["instance"]) + else + @impl.params.merge!(translate_create_opts(new_params)["server"]) + end + end + @impl.save + end + + def update(new_params = nil) + save(new_params) + end +end diff --git a/lib/rest_connection/rightscale/server_internal.rb b/lib/rest_connection/rightscale/server_internal.rb index 5fb01e9..b86b431 100644 --- a/lib/rest_connection/rightscale/server_internal.rb +++ b/lib/rest_connection/rightscale/server_internal.rb @@ -1,29 +1,38 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ -# -# You must have special API access to use these internal API calls. -# +# +# API 0.1 +# class ServerInternal include RightScale::Api::Base extend RightScale::Api::BaseExtend include SshHax - include RightScale::Api::Internal extend RightScale::Api::InternalExtend + deny_methods :index, :show, :create, :destroy, :update + def resource_plural_name "servers" end diff --git a/lib/rest_connection/rightscale/server_template.rb b/lib/rest_connection/rightscale/server_template.rb index 5c5ce4a..a575dcf 100644 --- a/lib/rest_connection/rightscale/server_template.rb +++ b/lib/rest_connection/rightscale/server_template.rb @@ -1,23 +1,50 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ -class ServerTemplate +# +# API 1.0 +# +class ServerTemplate include RightScale::Api::Base extend RightScale::Api::BaseExtend - def initialize(params) - @params = params + include RightScale::Api::Taggable + extend RightScale::Api::TaggableExtend + + attr_accessor :internal + + def initialize(*args, &block) + super(*args, &block) + if RightScale::Api::api0_1? + @internal = ServerTemplateInternal.new(*args, &block) + end + end + + def reload + ret = connection.get(URI.parse(self.href).path, :include_mcis => true) + @params ? @params.merge!(ret) : @params = ret + @params["multi_cloud_images"].map! { |mci_params| MultiCloudImage.new(mci_params) } + @params["default_multi_cloud_image"] = MultiCloudImage.new(@params["default_multi_cloud_image"]) + @params end def executables @@ -42,7 +69,7 @@ def fetch_alert_specs end @params["alert_specs"] = as end - + def multi_cloud_images unless @params["multi_cloud_images"] fetch_multi_cloud_images @@ -60,7 +87,15 @@ def fetch_executables end def fetch_multi_cloud_images - @params["multi_cloud_images"] = RsInternal.get_server_template_multi_cloud_images(self.href) + @params["multi_cloud_images"] = [] + ServerTemplateInternal.new(:href => self.href).multi_cloud_images.each { |mci_params| + @params["multi_cloud_images"] << MultiCloudImageInternal.new(mci_params) + } + mcis = McServerTemplate.find(self.rs_id.to_i).multi_cloud_images + @params["multi_cloud_images"].each_index { |i| + @params["multi_cloud_images"][i]["multi_cloud_image_cloud_settings"] += mcis[i].settings + } + @params["multi_cloud_images"] end # The RightScale api calls this 'duplicate' but is more popularly known as 'clone' from a users perspective diff --git a/lib/rest_connection/rightscale/server_template_internal.rb b/lib/rest_connection/rightscale/server_template_internal.rb index ff4dc68..015ba11 100644 --- a/lib/rest_connection/rightscale/server_template_internal.rb +++ b/lib/rest_connection/rightscale/server_template_internal.rb @@ -1,24 +1,37 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ -class ServerTemplateInternal +# +# API 0.1 +# +class ServerTemplateInternal include RightScale::Api::Base extend RightScale::Api::BaseExtend include RightScale::Api::Internal extend RightScale::Api::InternalExtend + deny_methods :index, :create, :show, :update, :destroy + def resource_plural_name "server_templates" end @@ -39,7 +52,7 @@ def add_multi_cloud_image(mci_href) t = URI.parse(self.href) connection.put(t.path + "/add_multi_cloud_image", :multi_cloud_image_href => mci_href) end - + def delete_multi_cloud_image(mci_href) t = URI.parse(self.href) connection.put(t.path + "/delete_multi_cloud_image", :multi_cloud_image_href => mci_href) @@ -75,7 +88,7 @@ def add_executable(executable, apply="operational") connection.post(t.path + "/add_executable", params) end - # <~Executable> executable, an Executable object to delete + # <~Executable> executable, an Executable object to delete # <~String> Apply, a string designating the type of executable: "boot", "operational", "decommission". Default is operational def delete_executable(executable, apply="operational") t = URI.parse(self.href) diff --git a/lib/rest_connection/rightscale/session.rb b/lib/rest_connection/rightscale/session.rb new file mode 100644 index 0000000..779aeeb --- /dev/null +++ b/lib/rest_connection/rightscale/session.rb @@ -0,0 +1,69 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +class Session + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + + deny_methods :index, :destroy, :update, :show + + def self.index + self.new(connection.get(resource_singular_name)) + end + + def self.create(opts={}) + settings = connection.settings + ignored, account = settings[:api_url].split(/\/acct\//) if settings[:api_url].include?("acct") + params = { + "email" => settings[:user], + "password" => settings[:pass], + "account_href" => "/api/accounts/#{account}" + }.merge(opts) + resp = connection.post(resource_singular_name, params) + connection.cookie = resp.response['set-cookie'] + end + + def self.accounts(opts={}) + settings = connection.settings + params = { + "email" => settings[:user], + "password" => settings[:pass], + }.merge(opts) + a = Array.new + connection.get(resource_singular_name + "/accounts").each do |object| + a << Account.new(object) + end + return a + end + + def self.create_instance_session + # TODO + end + + def self.index_instance_session + # TODO + end +end diff --git a/lib/rest_connection/rightscale/sqs_queue.rb b/lib/rest_connection/rightscale/sqs_queue.rb new file mode 100644 index 0000000..2ac238e --- /dev/null +++ b/lib/rest_connection/rightscale/sqs_queue.rb @@ -0,0 +1,32 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.0 +# +class SqsQueue + include RightScale::Api::Base + extend RightScale::Api::BaseExtend + + deny_methods :update +end diff --git a/lib/rest_connection/rightscale/status.rb b/lib/rest_connection/rightscale/status.rb index a91033a..9a7328e 100644 --- a/lib/rest_connection/rightscale/status.rb +++ b/lib/rest_connection/rightscale/status.rb @@ -1,23 +1,35 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + # # For now this is a stub for using with the ssh enabled Server#run_script #This is the v4 image only work status api. # was used by Server#run_script (depricating..) -class Status +# +# API 1.0 +# +class Status include RightScale::Api::Base extend RightScale::Api::BaseExtend def wait_for_completed(audit_link = "no audit link available", timeout = 900) diff --git a/lib/rest_connection/rightscale/tag.rb b/lib/rest_connection/rightscale/tag.rb index 6fed092..fc9e27e 100644 --- a/lib/rest_connection/rightscale/tag.rb +++ b/lib/rest_connection/rightscale/tag.rb @@ -1,22 +1,35 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ -class Tag +# +# API 1.0 +# +class Tag include RightScale::Api::Base extend RightScale::Api::BaseExtend + deny_methods :index, :create, :destroy, :update, :show + def self.search(resource_name, tags, opts=nil) parameters = { :resource_type => resource_name.to_s, :tags => tags } parameters.merge!(opts) unless opts.nil? diff --git a/lib/rest_connection/rightscale/task.rb b/lib/rest_connection/rightscale/task.rb new file mode 100644 index 0000000..ca1ac8d --- /dev/null +++ b/lib/rest_connection/rightscale/task.rb @@ -0,0 +1,57 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# +class Task + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + + deny_methods :index, :create, :destroy, :update + + def self.parse_args(cloud_id, instance_id) + "clouds/#{cloud_id}/instances/#{instance_id}/live/" + end + + def show + url = URI.parse(self.href) + @params.merge! connection.get(url.path)#, 'view' => "extended") + end + + def wait_for_state(state, timeout=900) + while(timeout > 0) + show + return true if self.summary.include?(state) + connection.logger("state is #{self.summary}, waiting for #{state}") + raise "FATAL error:\n\n #{self.summary} \n\n" if self.summary.include?('failed') # TODO #{self.detail} + sleep 30 + timeout -= 30 + end + raise "FATAL: Timeout waiting for Executable to complete. State was #{self.summary}" if timeout <= 0 + end + + def wait_for_completed(timeout=900) + wait_for_state("completed", timeout) + end +end diff --git a/lib/rest_connection/rightscale/user.rb b/lib/rest_connection/rightscale/user.rb new file mode 100644 index 0000000..12265f3 --- /dev/null +++ b/lib/rest_connection/rightscale/user.rb @@ -0,0 +1,41 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.5 +# + +# +# User Resource requires "admin" role +# + +class User + include RightScale::Api::Gateway + extend RightScale::Api::GatewayExtend + + deny_methods :destroy, :update + + def self.filters + [:email, :first_name, :last_name] + end +end diff --git a/lib/rest_connection/rightscale/vpc_dhcp_option.rb b/lib/rest_connection/rightscale/vpc_dhcp_option.rb new file mode 100644 index 0000000..20e1bf1 --- /dev/null +++ b/lib/rest_connection/rightscale/vpc_dhcp_option.rb @@ -0,0 +1,30 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +# +# API 1.0 +# +class VpcDhcpOption + include RightScale::Api::Base + extend RightScale::Api::BaseExtend +end diff --git a/lib/rest_connection/ssh_hax.rb b/lib/rest_connection/ssh_hax.rb index 5d5d675..8d78cf0 100644 --- a/lib/rest_connection/ssh_hax.rb +++ b/lib/rest_connection/ssh_hax.rb @@ -1,18 +1,25 @@ -# This file is part of RestConnection +#-- +# Copyright (c) 2010-2012 RightScale Inc # -# RestConnection is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: # -# RestConnection is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. # -# You should have received a copy of the GNU General Public License -# along with RestConnection. If not, see . - +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ require 'net/ssh' @@ -37,150 +44,81 @@ def ssh_key_config(item) ssh_keys end - def run_and_tail(run_this, tail_command, expect, ssh_key=nil, host_dns=self.dns_name) - status = nil - result = nil - output = "" - connection.logger("Running: #{run_this}") - Net::SSH.start(host_dns, 'root', :keys => ssh_key_config(ssh_key), :user_known_hosts_file => "/dev/null") do |ssh| - cmd_channel = ssh.open_channel do |ch1| - ch1.on_request('exit-status') do |ch, data| - status = data.read_long - end - ch1.exec(run_this) do |ch2, success| - unless success - output = "ERROR: SSH cmd failed to exec" - status = 1 - end - ch2.on_data do |ch, data| - output += data - end - ch2.on_extended_data do |ch, type, data| - output += data - end - - end - end - log_channel = ssh.open_channel do |ch2| - ch2.exec tail_command do |ch, success| - raise "could not execute command" unless success - # "on_data" is called when the process writes something to stdout - ch.on_data do |c, data| - output += data - if data =~ expect - result = $1 - end - end - # "on_extended_data" is called when the process writes something to stderr - ch.on_extended_data do |c, type, data| - #STDERR.print data - end - ch.on_close do - end - ch.on_process do |c| - if result - ch.close - ssh.exec("killall tail") - end - end - end - end - cmd_channel.wait - log_channel.wait - end - connection.logger output - success = result.include?('completed') - connection.logger "Converge failed. See server audit: #{self.audit_link}" unless success - return {:status => success, :output => output} - end - # script is an Executable object with minimally nick or id set - def run_executable_with_ssh(script, options={}, ssh_key=nil) - raise "FATAL: run_executable called on a server with no dns_name. You need to run .settings on the server to populate this attribute." unless self.dns_name - if script.is_a?(Executable) - script = script.right_script - end - raise "FATAL: unrecognized format for script. Must be an Executable or RightScript with href or name attributes" unless (script.is_a?(RightScript)) && (script.href || script.name) - if script.href - run_this = "rs_run_right_script -i #{script.href.split(/\//).last}" - elsif script.name - run_this = "rs_run_right_script -n #{script.name}" - end - tail_command ="tail -f -n1 /var/log/messages" - expect = /RightLink.*RS> ([completed|failed]+:)/ - options.each do |key, value| - run_this += " -p #{key}=#{value}" - end - AuditEntry.new(run_and_tail(run_this, tail_command, expect)) + def spot_check(command, ssh_key=nil, host_dns=self.reachable_ip, &block) + puts "SshHax::Probe method #{__method__}() entered..." + results = spot_check_command(command, ssh_key, host_dns) + yield results[:output] end - # recipe can be either a String, or an Executable - # host_dns is optional and will default to objects self.dns_name - def run_recipe_with_ssh(recipe, ssh_key=nil, host_dns=self.dns_name) - raise "FATAL: run_script called on a server with no dns_name. You need to run .settings on the server to populate this attribute." unless self.dns_name - if recipe.is_a?(Executable) - recipe = recipe.recipe - end - tail_command ="tail -f -n1 /var/log/messages" - expect = /RightLink.*RS> ([completed|failed]+: < #{recipe} >)/ - run_this = "rs_run_recipe -n '#{recipe}'" - run_and_tail(run_this, tail_command, expect, ssh_key) - end - def spot_check(command, ssh_key=nil, host_dns=self.dns_name, &block) - connection.logger "SSHing to #{host_dns}" - Net::SSH.start(host_dns, 'root', :keys => ssh_key_config(ssh_key)) do |ssh| - result = ssh.exec!(command) - yield result - end - end # returns true or false based on command success - def spot_check_command?(command, ssh_key=nil, host_dns=self.dns_name) + def spot_check_command?(command, ssh_key=nil, host_dns=self.reachable_ip) + puts "SshHax::Probe method #{__method__}() entered..." results = spot_check_command(command, ssh_key, host_dns) return results[:status] == 0 end + # returns hash of exit_status and output from command - def spot_check_command(command, ssh_key=nil, host_dns=self.dns_name) - raise "FATAL: spot_check_command called on a server with no dns_name. You need to run .settings on the server to populate this attribute." unless host_dns - connection.logger "SSHing to #{host_dns} using key(s) #{ssh_key_config(ssh_key)}" + # Note that "sudo" is prepended to and the 'rightscale' user is used. + def spot_check_command(command, ssh_key=nil, host_dns=self.reachable_ip, do_not_log_result=false) + puts "SshHax::Probe method #{__method__}() entered..." + raise "FATAL: spot_check_command called on a server with no reachable_ip. You need to run .settings on the server to populate this attribute." unless host_dns + connection.logger "SSHing to #{host_dns} using key(s) #{ssh_key_config(ssh_key).inspect}" status = nil output = "" success = false retry_count = 0 while (!success && retry_count < SSH_RETRY_COUNT) do - begin - Net::SSH.start(host_dns, 'root', :keys => ssh_key_config(ssh_key), :user_known_hosts_file => "/dev/null") do |ssh| - cmd_channel = ssh.open_channel do |ch1| - ch1.on_request('exit-status') do |ch, data| - status = data.read_long - end - ch1.exec(command) do |ch2, success| - unless success - status = 1 - end - ch2.on_data do |ch, data| - output += data - end - ch2.on_extended_data do |ch, type, data| - output += data + begin + # Test for ability to connect; Net::SSH.start sometimes hangs under certain server-side sshd configs + test_ssh = "" + [5, 15, 60].each { |timeout_max| + test_ssh = `ssh -ttq -o \"BatchMode=yes\" -o \"StrictHostKeyChecking=no\" -o \"ConnectTimeout #{timeout_max}\" rightscale@#{host_dns} -C \"exit\" 2>&1`.chomp + break if test_ssh =~ /permission denied/i or test_ssh.empty? + } + raise test_ssh unless test_ssh =~ /permission denied/i or test_ssh.empty? + + Net::SSH.start(host_dns, 'rightscale', :keys => ssh_key_config(ssh_key), :user_known_hosts_file => "/dev/null") do |ssh| + cmd_channel = ssh.open_channel do |ch1| + ch1.on_request('exit-status') do |ch, data| + status = data.read_long + end + # Request a pseudo-tty, this is needed as all calls use sudo to support RightLink 5.8 + ch1.request_pty do |ch, success| + raise "Could not obtain a pseudo-tty!" if !success + end + # Now execute the command with "sudo" prepended to it. + # NOTE: The use of single quotes is required to keep Ruby from interpretting the command string passed in and messing up regex's + sudo_command = 'sudo ' + command + puts 'SshHax::Probe executing ' + sudo_command + '...' + ch1.exec(sudo_command) do |ch2, success| + unless success + status = 1 + end + ch2.on_data do |ch, data| + output += data + end + ch2.on_extended_data do |ch, type, data| + output += data + end + end end end + rescue Exception => e + retry_count += 1 # opening the ssh channel failed -- try again. + connection.logger "ERROR during SSH session to #{host_dns}, retrying #{retry_count}: #{e} #{e.backtrace}" + sleep 10 + raise e unless retry_count < SSH_RETRY_COUNT end end - rescue Exception => e - retry_count += 1 # opening the ssh channel failed -- try again. - connection.logger "ERROR during SSH session to #{host_dns}, retrying #{retry_count}: #{e} #{e.backtrace}" - sleep 10 - end - end - connection.logger "SSH Run: #{command} on #{host_dns}. Retry was #{retry_count}. Exit status was #{status}. Output below ---\n#{output}\n---" + connection.logger "SSH Run: #{command} on #{host_dns}. Retry was #{retry_count}. Exit status was #{status}. Output below ---\n#{output}\n---" unless do_not_log_result + puts "SshHax::Probe method #{__method__}() exiting..." return {:status => status, :output => output} - end + end end - - diff --git a/lib/rest_connection/version.rb b/lib/rest_connection/version.rb new file mode 100644 index 0000000..2ec5afb --- /dev/null +++ b/lib/rest_connection/version.rb @@ -0,0 +1,4 @@ +# This gem is versioned with the usual X.Y.Z notation +module RestConnection + VERSION = '1.0.6' +end diff --git a/log_api_call_parser b/log_api_call_parser new file mode 100755 index 0000000..9a56864 --- /dev/null +++ b/log_api_call_parser @@ -0,0 +1,92 @@ +#!/usr/bin/ruby + +#-- +# Copyright (c) 2012 RightScale, Inc, All Rights Reserved Worldwide. +# +# THIS PROGRAM IS CONFIDENTIAL AND PROPRIETARY TO RIGHTSCALE +# AND CONSTITUTES A VALUABLE TRADE SECRET. Any unauthorized use, +# reproduction, modification, or disclosure of this program is +# strictly prohibited. Any use of this program by an authorized +# licensee is strictly subject to the terms and conditions, +# including confidentiality obligations, set forth in the applicable +# License Agreement between RightScale, Inc. and +# the licensee. +#++ + +######################################################################################################################## +# rest_connection API Call Parser Main program +# +# This program will parse a rest_connection.log produced from any given monkey run and display the: +# - API version +# - Request Type +# - Request +# in tabular format on STDOUT. It will also generate the csv file odf the same information. +######################################################################################################################## + +# Third party requires +require 'rubygems' +require 'trollop' +require 'terminal-table' +require 'tempfile' +require 'csv' + +# +# Parse and validate command line arguments +# +opts = Trollop::options do + banner <<-EOS +Usage: +log_api_call_parser [options] + where [options] are: + EOS + opt :input, "Path to the input file in standard rest_connection logger format", :short => "-i", :type => String, :required => true + opt :csv_file, "Path to the csv output file", :short => "-o", :type => String, :required => true +end + +# Open the input file and read it into an array +input_file = File.open(opts[:input]) +log_file_array = input_file.readlines +input_file.close + +# Loop over the array and for each row, parse out the api version, request type and the request +# and add them to output_array for later sorting and writing. +output_array = [] +search_string = "INFO -- :" +log_file_array.each { |element| + if element[0..2] == "I, " + # Find the API version start column as this can vary at least per log file and maybe even per line + api_version_start_column = element.index(search_string) + api_version_start_column = api_version_start_column + search_string.length + 1 + + # Get the elements + element = element[api_version_start_column..-1] + version = element[5..8] + request_type = element[11..13] + if request_type == "GET" || request_type == "PUT" + request = element[16..-1] + output_array.push "#{version},#{request_type},#{request}" + end + end +} + +# Sort the output array by API version +output_array.sort! {|a,b| a <=> b} + +# Remove duplicates +output_array.uniq! + +# Write out the CSV file +csv_file = File.open(opts[:csv_file], "w") +csv_file_name = csv_file.path() +output_array.each { |element| csv_file.puts element } +csv_file.close + +# Generate a table version and write that out to STDOUT +csv_array = CSV.read(csv_file_name) +table = Terminal::Table.new +csv_array.collect do |line| + table << line + table << :separator +end +puts table + diff --git a/rest_connection.gemspec b/rest_connection.gemspec index 1b6effc..ed81a5b 100644 --- a/rest_connection.gemspec +++ b/rest_connection.gemspec @@ -1,100 +1,32 @@ -# Generated by jeweler -# DO NOT EDIT THIS FILE DIRECTLY -# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' -# -*- encoding: utf-8 -*- +require File.expand_path('../lib/rest_connection/version', __FILE__) Gem::Specification.new do |s| - s.name = %q{rest_connection} - s.version = "0.0.22" + s.name = 'rest_connection' + s.version = RestConnection::VERSION + s.platform = Gem::Platform::RUBY + s.date = Time.now.utc.strftime("%Y-%m-%d") + s.require_path = 'lib' + s.authors = [ 'RightScale, Inc.' ] + s.email = [ 'rubygems@rightscale.com' ] + s.homepage = 'https://github.com/rightscale/rest_connection' + s.summary = 'A Modular RESTful API library.' + s.description = %{ +The rest_connection gem simplifies the use of RESTful APIs. +It currently has support for RightScale API 1.0 and 1.5. + } + s.files = `git ls-files`.split(' ') + s.test_files = `git ls-files spec config`.split(' ') + s.rubygems_version = '1.8.24' - s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= - s.authors = ["Jeremy Deininger"] - s.date = %q{2011-09-10} - s.description = %q{provides rest_connection} - s.email = %q{jeremy@rubyonlinux.org} - s.extra_rdoc_files = [ - "README.rdoc" - ] - s.files = [ - "README.rdoc", - "Rakefile", - "VERSION", - "config/rest_api_config.yaml.sample", - "examples/README.txt", - "examples/console.rb", - "examples/cucumber/2mysql_5.x_v2_beta_from_scratch.feature", - "examples/cucumber/step_definitions/deployment_steps.rb", - "examples/cucumber/step_definitions/mysql_steps.rb", - "examples/cucumber/step_definitions/recipe_steps.rb", - "examples/cucumber/step_definitions/spot_check_steps.rb", - "examples/relaunch_deployment.rb", - "examples/right_scale_ec2_instances_api_test.rb", - "lib/rest_connection.rb", - "lib/rest_connection/rightscale/alert_spec.rb", - "lib/rest_connection/rightscale/audit_entry.rb", - "lib/rest_connection/rightscale/credential.rb", - "lib/rest_connection/rightscale/deployment.rb", - "lib/rest_connection/rightscale/ec2_ebs_snapshot.rb", - "lib/rest_connection/rightscale/ec2_ebs_volume.rb", - "lib/rest_connection/rightscale/ec2_elastic_ip.rb", - "lib/rest_connection/rightscale/ec2_security_group.rb", - "lib/rest_connection/rightscale/ec2_server_array.rb", - "lib/rest_connection/rightscale/ec2_ssh_key.rb", - "lib/rest_connection/rightscale/ec2_ssh_key_internal.rb", - "lib/rest_connection/rightscale/executable.rb", - "lib/rest_connection/rightscale/instance.rb", - "lib/rest_connection/rightscale/mc_server.rb", - "lib/rest_connection/rightscale/multi_cloud_image.rb", - "lib/rest_connection/rightscale/multi_cloud_image_cloud_setting_internal.rb", - "lib/rest_connection/rightscale/multi_cloud_image_internal.rb", - "lib/rest_connection/rightscale/right_script.rb", - "lib/rest_connection/rightscale/right_script_internal.rb", - "lib/rest_connection/rightscale/rightscale_api_base.rb", - "lib/rest_connection/rightscale/rightscale_api_gateway.rb", - "lib/rest_connection/rightscale/rightscale_api_internal.rb", - "lib/rest_connection/rightscale/rightscale_api_resources.rb", - "lib/rest_connection/rightscale/rs_internal.rb", - "lib/rest_connection/rightscale/server.rb", - "lib/rest_connection/rightscale/server_internal.rb", - "lib/rest_connection/rightscale/server_template.rb", - "lib/rest_connection/rightscale/server_template_internal.rb", - "lib/rest_connection/rightscale/status.rb", - "lib/rest_connection/rightscale/tag.rb", - "lib/rest_connection/ssh_hax.rb", - "rest_connection.gemspec", - "spec/ec2_server_array_spec.rb", - "spec/ec2_ssh_key_internal_spec.rb", - "spec/image_jockey.rb", - "spec/method_missing_spec.rb", - "spec/right_script_internal.rb", - "spec/rs_internal_spec.rb", - "spec/server_internal_spec.rb", - "spec/server_spec.rb", - "spec/server_template_internal.rb", - "spec/spec_helper.rb", - "spec/tag_spec.rb" - ] - s.homepage = %q{http://github.com/jeremyd/rest_connection} - s.require_paths = ["lib"] - s.rubygems_version = %q{1.6.2} - s.summary = %q{lib for restful connections to the rightscale api} + s.add_runtime_dependency 'activesupport' + s.add_runtime_dependency 'net-ssh' + s.add_runtime_dependency 'json' + s.add_runtime_dependency 'highline' + s.add_runtime_dependency 'rest-client' + s.add_runtime_dependency 'nokogiri' - if s.respond_to? :specification_version then - s.specification_version = 3 - - if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then - s.add_runtime_dependency(%q, ["= 2.3.10"]) - s.add_runtime_dependency(%q, [">= 0"]) - s.add_runtime_dependency(%q, [">= 0"]) - else - s.add_dependency(%q, ["= 2.3.10"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - end - else - s.add_dependency(%q, ["= 2.3.10"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - end + s.add_development_dependency 'rake', '0.8.7' + s.add_development_dependency 'bundler' + s.add_development_dependency 'rspec', '1.3.0' + s.add_development_dependency 'ruby-debug' end - diff --git a/rest_connection.rconf b/rest_connection.rconf new file mode 100644 index 0000000..d260025 --- /dev/null +++ b/rest_connection.rconf @@ -0,0 +1,10 @@ +ruby do + version 'ree-1.8.7-2012.02' + rubygems '1.8.24' + gemset 'rest_connection' +end +bundler do + version '1.2.2' + exclusions 'deployment' + bundle_path File.join(ENV["HOME"], '.rightscale_bundle', 'rest_connection') +end diff --git a/spec/ec2_server_array_spec.rb b/spec/ec2_server_array_spec.rb index 76ab310..47728ed 100644 --- a/spec/ec2_server_array_spec.rb +++ b/spec/ec2_server_array_spec.rb @@ -1,6 +1,28 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + require 'rubygems' require 'rest_connection' -require 'spec' require 'ruby-debug' describe Ec2ServerArray, "takes over the world with some server arrays" do diff --git a/spec/ec2_ssh_key_internal_spec.rb b/spec/ec2_ssh_key_internal_spec.rb index c28945b..0c4b794 100644 --- a/spec/ec2_ssh_key_internal_spec.rb +++ b/spec/ec2_ssh_key_internal_spec.rb @@ -1,6 +1,28 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + require 'rubygems' require 'rest_connection' -require 'spec' require 'ruby-debug' describe Ec2SshKeyInternal, "ec2_ssh_key internal api object exercise" do diff --git a/spec/image_jockey.rb b/spec/image_jockey.rb index 5ba42e8..e9167aa 100644 --- a/spec/image_jockey.rb +++ b/spec/image_jockey.rb @@ -1,6 +1,28 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + require 'rubygems' require 'rest_connection' -require 'spec' describe MultiCloudImageInternal, "exercises the mci internal api" do diff --git a/spec/mcserver_spec.rb b/spec/mcserver_spec.rb new file mode 100644 index 0000000..23e3d60 --- /dev/null +++ b/spec/mcserver_spec.rb @@ -0,0 +1,39 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +require 'rubygems' +require 'rest_connection' +require 'ruby-debug' + +describe McServer, "server api object exercise" do + before(:all) do + @mcserver_v5 = McServer.find("/api/clouds/850/instances/AA5AOKVUOJPC9") # a v5 server + end + + it "should run a recipe on a v5 server" do + this_template = ServerTemplate.find(@mcserver_v5.server_template_href) + run_first = this_template.executables.first + audit = @mcserver_v5.run_executable(run_first) + audit.wait_for_completed + end +end diff --git a/spec/method_missing_spec.rb b/spec/method_missing_spec.rb index 082f038..46ca3cb 100644 --- a/spec/method_missing_spec.rb +++ b/spec/method_missing_spec.rb @@ -1,6 +1,28 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + require 'rubygems' require 'rest_connection' -require 'spec' require 'ruby-debug' describe Server, "using a server" do diff --git a/spec/multi.rb b/spec/multi.rb new file mode 100644 index 0000000..c89c9fd --- /dev/null +++ b/spec/multi.rb @@ -0,0 +1,38 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +require 'rubygems' +require 'rest_connection' +require 'ruby-debug' + +describe MultiCloudImage do + it "goes" do + + mci = MultiCloudImage.find(46563) + mci = MultiCloudImage.find(57499) + settings = mci.find_and_flatten_settings + debugger +puts "blah" + + end +end diff --git a/spec/right_script_internal.rb b/spec/right_script_internal.rb index 329229f..faf7ef3 100644 --- a/spec/right_script_internal.rb +++ b/spec/right_script_internal.rb @@ -1,3 +1,26 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + require File.join(File.dirname(__FILE__), 'spec_helper') require 'ruby-debug' diff --git a/spec/rs_internal_spec.rb b/spec/rs_internal_spec.rb index 5d7df65..a07c0e1 100644 --- a/spec/rs_internal_spec.rb +++ b/spec/rs_internal_spec.rb @@ -1,6 +1,28 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + require 'rubygems' require 'rest_connection' -require 'spec' require 'ruby-debug' describe RsInternal, "exercises the rs_internal api" do diff --git a/spec/runtests b/spec/runtests new file mode 100755 index 0000000..07040dc --- /dev/null +++ b/spec/runtests @@ -0,0 +1,9 @@ +#!/bin/bash +for file in ./*.rb +do + if [ "${file}" != "./spec_helper.rb" ] + then + echo "**** Running ${file}..." + rspec ${file} + fi +done diff --git a/spec/server_internal_spec.rb b/spec/server_internal_spec.rb index 7a440cc..d725f8f 100644 --- a/spec/server_internal_spec.rb +++ b/spec/server_internal_spec.rb @@ -1,6 +1,28 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + require 'rubygems' require 'rest_connection' -require 'spec' require 'ruby-debug' describe ServerInternal, "server internal api object exercise" do diff --git a/spec/server_spec.rb b/spec/server_spec.rb index c61dbd3..766c70b 100644 --- a/spec/server_spec.rb +++ b/spec/server_spec.rb @@ -1,6 +1,28 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + require 'rubygems' require 'rest_connection' -require 'spec' require 'ruby-debug' describe Server, "server api object exercise" do diff --git a/spec/server_template_internal.rb b/spec/server_template_internal.rb index bb53398..47ab648 100644 --- a/spec/server_template_internal.rb +++ b/spec/server_template_internal.rb @@ -1,3 +1,26 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + require File.join(File.dirname(__FILE__), 'spec_helper') require 'ruby-debug' diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index effc892..d0f308a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,7 +1,29 @@ +#-- +# Copyright (c) 2010-2012 RightScale Inc +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + $LOAD_PATH.unshift(File.dirname(__FILE__)) $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) require 'rubygems' require 'rest_connection' -require 'spec' require 'fileutils' require 'logger' diff --git a/spec/tag_spec.rb b/spec/tag_spec.rb index 0f2c295..1216c5d 100644 --- a/spec/tag_spec.rb +++ b/spec/tag_spec.rb @@ -1,6 +1,5 @@ require 'rubygems' require 'rest_connection' -require 'spec' require 'ruby-debug' describe Tag, "tags" do