diff --git a/Gemfile b/Gemfile index 0153879..3c746e2 100644 --- a/Gemfile +++ b/Gemfile @@ -9,6 +9,7 @@ gem 'mysql' gem 'resque', '~> 1.19' gem 'will_paginate', '~> 3.0' gem 'cloudservers' +gem 'openstack-compute' # Gems used only for assets and not required # in production environments by default. diff --git a/Gemfile.lock b/Gemfile.lock index 70504c2..7dcd633 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -53,6 +53,8 @@ GEM mime-types (1.17.2) multi_json (1.0.4) mysql (2.8.1) + openstack-compute (1.1.5) + json polyglot (0.3.3) rack (1.3.6) rack-cache (1.1) @@ -125,6 +127,7 @@ DEPENDENCIES cloudservers coffee-rails (~> 3.1.1) mysql + openstack-compute rails (= 3.1.1) resque (~> 1.19) sass-rails (~> 3.1.4) diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb index eee8b12..05105de 100644 --- a/app/controllers/accounts_controller.rb +++ b/app/controllers/accounts_controller.rb @@ -45,8 +45,8 @@ def update def limits @account = Account.find(params[:id]) - cs=CloudServersUtil.new(@account.cloud_servers_username, @account.cloud_servers_api_key) - render :text => cs.account_limits.to_json, :status => "200" + conn = @account.get_connection + render :text => conn.account_limits.to_json, :status => "200" end diff --git a/app/helpers/servers_helper.rb b/app/helpers/servers_helper.rb index 6c7d267..d00e5aa 100644 --- a/app/helpers/servers_helper.rb +++ b/app/helpers/servers_helper.rb @@ -13,19 +13,19 @@ def image_name(id) def flavor_name(id) return case id - when 1 + when "1" "256MB" - when 2 + when "2" "512MB" - when 3 + when "3" "1GB" - when 4 + when "4" "2GB" - when 5 + when "5" "4GB" - when 6 + when "6" "8GB" - when 7 + when "7" "15.5GB" else "Unknown" diff --git a/app/models/account.rb b/app/models/account.rb index c6c8b13..ce621a3 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -1,5 +1,17 @@ +require 'rackspace_connection' + class Account < ActiveRecord::Base - belongs_to :user + belongs_to :user + + def get_connection + if self.connection_type == 'rackspace' then + return RackspaceConnection.new(self.username, self.api_key, self.auth_url) + elsif self.connection_type == 'openstack' then + return OpenstackConnection.new(self.username, self.api_key, self.auth_url) + else + raise "Unsupported account connection type." + end + end end diff --git a/app/models/image.rb b/app/models/image.rb index aa6bc5b..218c2c9 100644 --- a/app/models/image.rb +++ b/app/models/image.rb @@ -8,7 +8,7 @@ class Image < ActiveRecord::Base def self.sync(user) acct=user.account - conn = CloudServersUtil.new(acct.cloud_servers_username, acct.cloud_servers_api_key) + conn = acct.get_connection image_refs = [] diff --git a/app/models/linux_server.rb b/app/models/linux_server.rb index 463d215..f827293 100644 --- a/app/models/linux_server.rb +++ b/app/models/linux_server.rb @@ -1,5 +1,4 @@ require 'logger' -require 'cloud_servers_util' require 'async_exec' require 'openvpn_config/server' require 'openvpn_config/client' @@ -70,8 +69,7 @@ def create_openvpn_client # server is online but can't ping OpenVPN servers .10 IP if not ping_test(vpn_server.internal_ip_addr) then - cs_conn=self.cloud_server_init - cs_conn.reboot_server(self.cloud_server_id_number) + self.account_connection.reboot_server(self.cloud_server_id_number) self.add_error_message("Server failed ping test.") self.retry_count += 1 self.save @@ -127,20 +125,20 @@ def create_openvpn_client # method to block until a server is online def loop_until_server_online - cs_conn=self.cloud_server_init + conn = self.account_connection - error_message="Failed to build server." + error_message = "Failed to build server." - timeout=self.server_online_timeout-(Time.now-self.updated_at).to_i + timeout = self.server_online_timeout-(Time.now-self.updated_at).to_i timeout = 120 if timeout < 120 begin Timeout::timeout(timeout) do # poll the server until progress is 100% - cs=cs_conn.find_server("#{self.cloud_server_id_number}") - until cs.progress == 100 and cs.status == "ACTIVE" do - cs=cs_conn.find_server("#{self.cloud_server_id_number}") + cs = conn.get_server(self.cloud_server_id_number) + until cs[:progress] == 100 and cs[:status] == "ACTIVE" do + cs = conn.get_server(self.cloud_server_id_number) sleep 1 end @@ -149,7 +147,7 @@ def loop_until_server_online if ! system(%{ COUNT=0 - while ! ssh -o "StrictHostKeyChecking no" -T -i #{self.server_group.ssh_key_basepath} root@#{cs.addresses[:public][0]} /bin/true > /dev/null 2>&1; do + while ! ssh -o "StrictHostKeyChecking no" -T -i #{self.server_group.ssh_key_basepath} root@#{cs[:public_ip]} /bin/true > /dev/null 2>&1; do if (($COUNT > 23)); then exit 1 fi diff --git a/app/models/server.rb b/app/models/server.rb index 0233b9f..636c67f 100644 --- a/app/models/server.rb +++ b/app/models/server.rb @@ -1,6 +1,5 @@ require 'logger' require 'async_exec' -require 'cloud_servers_util' require 'timeout' require 'util/ip_validator' require 'base64' @@ -19,8 +18,6 @@ class Server < ActiveRecord::Base belongs_to :server_group belongs_to :account validates_presence_of :name, :description, :flavor_id, :image_id, :server_group_id, :account_id - validates_numericality_of :flavor_id, :image_id, :server_group_id - validates_numericality_of :cloud_server_id_number, :if => :cloud_server_id_number has_many :vpn_network_interfaces, :as => :interfacable, :dependent => :destroy has_many :server_errors validates_format_of :name, :with => /^[A-Za-z0-9\-\.]+$/, :message => "Server name must use valid hostname characters (A-Z, a-z, 0-9, dash)." @@ -172,16 +169,23 @@ def create_cloud_server(schedule_client_openvpn=false) server_name_prefix=ENV['RACKSPACE_CLOUD_SERVER_NAME_PREFIX'] end - cs_conn=self.cloud_server_init + conn = self.account_connection retry_suffix=self.retry_count > 0 ? "#{rand(10)}-#{self.retry_count}" : "#{rand(10)}" - cs=cs_conn.create_cloud_server("#{server_name_prefix}#{self.name}-#{self.server_group_id}-#{retry_suffix}", self.image_id, self.flavor_id, generate_personalities) + server_id, admin_password = conn.create_server("#{server_name_prefix}#{self.name}-#{self.server_group_id}-#{retry_suffix}", self.image_id, self.flavor_id, generate_personalities) @tmp_files.each {|f| f.close(true)} #Remove tmp personalities files #harvest server ID and IP information - self.cloud_server_id_number = cs.id - self.external_ip_addr = cs.addresses[:public][0] - self.internal_ip_addr = cs.addresses[:private][0] - self.admin_password = cs.adminPass if is_windows + self.cloud_server_id_number = server_id + self.admin_password = admin_password if is_windows + save! + + server = conn.get_server(server_id) + until server[:public_ip] and server[:private_ip] do + server = conn.get_server(server_id) + sleep 1 + end + self.external_ip_addr = server[:public_ip] + self.internal_ip_addr = server[:private_ip] save! # if this server is an OpenVPN server create it now @@ -196,13 +200,13 @@ def create_cloud_server(schedule_client_openvpn=false) rescue Exception => e self.retry_count += 1 self.status = "Pending" # keep status set to pending - +# long_error_message=nil begin long_error_message="#{e.message}: #{e.response_body}" rescue end - +# if e and e.message and long_error_message then self.add_error_message("Failed to create cloud server: #{e.message}", long_error_message) elsif e and e.message then @@ -213,6 +217,7 @@ def create_cloud_server(schedule_client_openvpn=false) save! sleep 10 AsyncExec.run_job(CreateCloudServer, self.id, false) + end end @@ -224,8 +229,7 @@ def delete_cloud_server(cloud_server_id) until deleted or retry_count >= 3 do begin retry_count += 1 - cs_conn=self.cloud_server_init - cs_conn.destroy_server(cloud_server_id) + self.account_connection.destroy_server(cloud_server_id) deleted = true rescue # ignore all exceptions on delete @@ -255,9 +259,9 @@ def rebuild end end - def cloud_server_init + def account_connection acct=Account.find(self.account_id) - CloudServersUtil.new(acct.cloud_servers_username, acct.cloud_servers_api_key) + acct.get_connection end def add_error_message(message, long_error_message=message) diff --git a/app/models/user.rb b/app/models/user.rb index c2875a6..385c853 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,5 +1,4 @@ require 'digest/sha1' -require 'cloud_servers_util' class User < ActiveRecord::Base @@ -33,9 +32,9 @@ def handle_validate_on_create else begin acct=self.account - CloudServersUtil.new(acct.cloud_servers_username, acct.cloud_servers_api_key) + acct.get_connection rescue Exception => e - errors[:base] << "Invalid cloud servers username or api key specified: #{e.message}" + errors[:base] << "Unable to connect with cloud account credentials: #{e.message}" end end diff --git a/app/models/windows_server.rb b/app/models/windows_server.rb index 9b7018c..f52a005 100644 --- a/app/models/windows_server.rb +++ b/app/models/windows_server.rb @@ -1,6 +1,5 @@ require 'logger' require 'async_exec' -require 'cloud_servers_util' require 'openvpn_config/server' require 'openvpn_config/client' require 'util/ssh' @@ -38,8 +37,7 @@ def create_openvpn_client # server is online but can't ping OpenVPN servers .10 IP if not ping_test(vpn_server.internal_ip_addr) then - cs_conn=self.cloud_server_init - cs_conn.reboot_server(self.cloud_server_id_number) + self.account_connection.reboot_server(self.cloud_server_id_number) self.add_error_message("Server failed ping test.") self.retry_count += 1 self.save @@ -188,11 +186,11 @@ def configure_openvpn_client(client_key, client_cert, ca_cert) # method to block until a server is online def loop_until_server_online - cs_conn=self.cloud_server_init + conn = self.account_connection - error_message="Failed to build server." + error_message = "Failed to build server." - timeout=self.windows_server_online_timeout-(Time.now-self.updated_at).to_i + timeout = self.windows_server_online_timeout-(Time.now-self.updated_at).to_i timeout = 2000 if self.image_id == 58 # FIXME remove this when image customization support and settings are added timeout = 120 if timeout < 120 @@ -200,9 +198,9 @@ def loop_until_server_online Timeout::timeout(timeout) do # poll the server until progress is 100% - cs=cs_conn.find_server("#{self.cloud_server_id_number}") - until cs.progress == 100 and cs.status == "ACTIVE" do - cs=cs_conn.find_server("#{self.cloud_server_id_number}") + cs = conn.get_server(self.cloud_server_id_number) + until cs[:progress] == 100 and cs[:status] == "ACTIVE" do + cs = conn.get_server(self.cloud_server_id_number) sleep 1 end diff --git a/app/views/auth/index.html.erb b/app/views/auth/index.html.erb index 94db4ea..1d353c1 100644 --- a/app/views/auth/index.html.erb +++ b/app/views/auth/index.html.erb @@ -63,8 +63,8 @@ $(document).ready(function() { $("#user-dialog").dialog({ modal: true, - height: 500, - width: 600, + height: $(window).height()-50, + width: $(window).width()-50, buttons: { "Create": function() { user_create(); } } diff --git a/app/views/help/index.html.erb b/app/views/help/index.html.erb index 5e92130..b004151 100644 --- a/app/views/help/index.html.erb +++ b/app/views/help/index.html.erb @@ -104,21 +104,21 @@ Create an XML file containing the following example data. Name the file example. <server> <name>boot1</name> <description>Boot 1</description> - <flavor-id type="integer">2</flavor-id> - <image-id type="integer">14</image-id> + <flavor-id>2</flavor-id> + <image-id>14</image-id> <openvpn-server type="boolean">true</openvpn-server> </server> <server> <name>gate1</name> <description>Gate 1</description> - <flavor-id type="integer">2</flavor-id> - <image-id type="integer">14</image-id> + <flavor-id>2</flavor-id> + <image-id>14</image-id> </server> <server> <name>relay1</name> <description>Gate 1</description> - <flavor-id type="integer">2</flavor-id> - <image-id type="integer">14</image-id> + <flavor-id>2</flavor-id> + <image-id>14</image-id> </server> </servers> <ssh-public-keys type="array"> diff --git a/app/views/images/edit.html.erb b/app/views/images/edit.html.erb index a45de92..c0ca420 100644 --- a/app/views/images/edit.html.erb +++ b/app/views/images/edit.html.erb @@ -30,7 +30,7 @@ $(document).ready(function() {

<%= f.label :image_ref %>
- <%= f.text_field :image_ref, :class => "image_edit" %> + <%= f.text_field :image_ref, :class => "image_edit", :size => 60 %>

diff --git a/app/views/server_groups/_table.html.erb b/app/views/server_groups/_table.html.erb index 8fc5551..fc0b39c 100644 --- a/app/views/server_groups/_table.html.erb +++ b/app/views/server_groups/_table.html.erb @@ -26,8 +26,8 @@ <%= server_group.id %> <%= raw chop_for_html(server_group.name) %> <%= raw chop_for_html(server_group.description) %> -<% if server_group.user.account.cloud_servers_username then %> - <%= raw chop_for_html(server_group.user.account.cloud_servers_username) %> +<% if server_group.user.account.username then %> + <%= raw chop_for_html(server_group.user.account.username) %> <% else %>   <% end %> diff --git a/app/views/server_groups/new.html.erb b/app/views/server_groups/new.html.erb index 40aa7c9..0eb8206 100644 --- a/app/views/server_groups/new.html.erb +++ b/app/views/server_groups/new.html.erb @@ -10,7 +10,7 @@ $(document).ready(function() { } ); -<% if @account.cloud_servers_api_key.blank? then %> +<% if @account.api_key.blank? then %> $("#server-group-error-messages").css("display", "inline"); $("#server-group-error-messages-content").html("Please enter your cloud servers account credentials under 'settings'."); <% end %> diff --git a/app/views/servers/_table.html.erb b/app/views/servers/_table.html.erb index f3dc577..b85a1b4 100644 --- a/app/views/servers/_table.html.erb +++ b/app/views/servers/_table.html.erb @@ -29,14 +29,14 @@ "> <%= raw chop_for_html(server.server_group.name, 20) %>(<%= server.server_group.id %>) <%= raw chop_for_html(server.server_group.owner_name) %> - <%= raw chop_for_html(server.account.cloud_servers_username) %> + <%= raw chop_for_html(server.account.username) %> <%= raw chop_for_html(server.name) %> <% if server.external_ip_addr or server.internal_ip_addr %> <%= server.external_ip_addr %>/<%= server.internal_ip_addr %> <% else %> N/A <% end %> - <%= server.cloud_server_id_number %> + <%= raw chop_for_html(server.cloud_server_id_number, 15) %> <%= flavor_name(server.flavor_id) %> <%= raw chop_for_html(image_name(server.image_id), 15) %> <%= raw check_or_blank(server.openvpn_server) %> diff --git a/app/views/servers/_table_history.html.erb b/app/views/servers/_table_history.html.erb index f81ecf5..7afd011 100644 --- a/app/views/servers/_table_history.html.erb +++ b/app/views/servers/_table_history.html.erb @@ -28,10 +28,10 @@ "> <%= raw chop_for_html(server.server_group.name, 20) %>(<%= server.server_group.id %>) <%= raw chop_for_html(server.server_group.owner_name) %> - <%= raw chop_for_html(server.account.cloud_servers_username) %> + <%= raw chop_for_html(server.account.username) %> <%= raw chop_for_html(server.name) %> <%= timestamp(server.created_at.in_time_zone) %> - <%= server.cloud_server_id_number %> + <%= raw chop_for_html(server.cloud_server_id_number, 15) %> <%= flavor_name(server.flavor_id) %> <%= raw chop_for_html(image_name(server.image_id), 15) %> <%= raw check_or_blank(server.openvpn_server) %> diff --git a/app/views/users/_account.html.erb b/app/views/users/_account.html.erb index 8a18fa5..e234ed2 100644 --- a/app/views/users/_account.html.erb +++ b/app/views/users/_account.html.erb @@ -26,7 +26,7 @@ function my_account() { success: function(data) { $("#my-account-error-messages").css("display", "none"); $("#my-account-error-messages-content").html(""); - if ($("cloud-servers-username", data).text() != "") { + if ($("username", data).text() != "") { reset_settings_message(); append_settings_message('Cloud Servers Account information updated.'); } else { @@ -64,10 +64,14 @@ function my_account() { <%= form_for(@account, :html => { :id => "my-account-form", :onsubmit => "my_account(); return false;"}) do |f| %>

- <%= f.label :cloud_servers_username %>
- <%= f.text_field :cloud_servers_username, :class => "myaccount" %>
- <%= f.label :cloud_servers_api_key %>
- <%= f.text_field :cloud_servers_api_key, :class => "myaccount" %>
+ <%= f.label :connection_type %>: <%= f.radio_button :connection_type, 'rackspace', :checked => true %>Rackspace / <%= f.radio_button :connection_type, 'openstack' %>OpenStack
+
+ <%= f.label :username %>:
+ <%= f.text_field :username, :class => "myaccount" %>
+ <%= f.label :api_key %>:
+ <%= f.text_field(:api_key, {:class => "myaccount", :size => 60}) %>
+ <%= f.label :auth_url %>: (required for OpenStack/optional for Rackspace)
+ <%= f.text_field(:auth_url, {:class => "myaccount", :size => 80}) %>
<%= f.submit 'Update Account' %>

diff --git a/app/views/users/_api_limits.html.erb b/app/views/users/_api_limits.html.erb index fe11904..4bba931 100644 --- a/app/views/users/_api_limits.html.erb +++ b/app/views/users/_api_limits.html.erb @@ -11,8 +11,6 @@ function load_my_api_limits() { var obj = jQuery.parseJSON(data); limit_html=""; limit_html+=""; limit_html+=""; diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb index bbbee92..0aeb7c6 100644 --- a/app/views/users/new.html.erb +++ b/app/views/users/new.html.erb @@ -24,30 +24,28 @@ $(document).ready(function() { <%= form_for(@user, :html => { :id => "user-create-form", :onsubmit => "user_create(); return false;"}) do |f| %>

- <%= f.label :username %>
- <%= f.text_field :username, :class => "user_new" %> + <%= f.label :username %>:<%= f.text_field :username, :class => "user_new" %>

- <%= f.label :first_name %>
- <%= f.text_field :first_name, :class => "user_new" %> + <%= f.label :first_name %>:<%= f.text_field :first_name, :class => "user_new" %>

- <%= f.label :last_name %>
- <%= f.text_field :last_name, :class => "user_new" %> + <%= f.label :last_name %>:<%= f.text_field :last_name, :class => "user_new" %>


- <%= f.label :password %>
- <%= f.password_field :password, :class => "user_new" %>
- <%= f.label :password_confirmation %>
- <%= f.password_field :password_confirmation, :class => "user_new" %> + <%= f.label :password %>:<%= f.password_field :password, :class => "user_new" %>
+ <%= f.label :password_confirmation %>:<%= f.password_field :password_confirmation, :class => "user_new" %>


+API Credentials

-

-
+ :Rackspace / Openstack

+

+

+

<% end %> diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index 06b69ac..136dcff 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -48,7 +48,7 @@ $(document).ready(function() {
-

Cloud Servers Account

+

API Credentials

<%= render :partial => 'account' %>
diff --git a/contrib/runner_scripts/cleanup_servers.rb b/contrib/runner_scripts/cleanup_servers.rb index d50d62c..93159bf 100644 --- a/contrib/runner_scripts/cleanup_servers.rb +++ b/contrib/runner_scripts/cleanup_servers.rb @@ -8,22 +8,21 @@ Account.find(:all, :conditions => ["cloud_servers_username IS NOT NULL AND cloud_servers_username != '' and cloud_servers_api_key IS NOT NULL and cloud_servers_api_key != ''"], :group => "cloud_servers_api_key").each do |acct| begin - cs_conn=CloudServersUtil.new(acct.cloud_servers_username, acct.cloud_servers_api_key) - cs_conn.all_servers do |cs| + conn = acct.get_connection + conn.all_servers do |server| - exp=Regexp.new("^#{CS_NAME_PREFIX}") - if cs[:name] =~ exp then + exp = Regexp.new("^#{CS_NAME_PREFIX}") + if server[:name] =~ exp then - server=Server.find(:first, :conditions => ["cloud_server_id_number = ? AND historical = 0", cs[:id]]) + server = Server.find(:first, :conditions => ["cloud_server_id_number = ? AND historical = 0", server[:id]]) if server.nil? then begin - puts "Account: #{acct.cloud_servers_username}, Deleting cloud server ID: #{cs[:id]} #{cs[:name]}" + puts "Account: #{acct.cloud_servers_username}, Deleting cloud server ID: #{server[:id]} #{server[:name]}" Timeout::timeout(30) do - cs_server=cs_conn.find_server(cs[:id]) - cs_server.update(:name => "deleted_#{cs[:id]}") - cs_server.delete! + conn.update_server(server[:id], {:name => "deleted_#{server[:id]}"}) + conn.delete_server(server[:id]) end rescue end diff --git a/db/migrate/028_update_accounts2.rb b/db/migrate/028_update_accounts2.rb new file mode 100644 index 0000000..8809b5a --- /dev/null +++ b/db/migrate/028_update_accounts2.rb @@ -0,0 +1,17 @@ +class UpdateAccounts2 < ActiveRecord::Migration + + def self.up + add_column :accounts, :connection_type, :string, :default => "rackspace" + add_column :accounts, :auth_url, :string, :default => "" + rename_column :accounts, :cloud_servers_username, :username + rename_column :accounts, :cloud_servers_api_key, :api_key + end + + def self.down + remove_column :accounts, :connection_type + remove_column :accounts, :auth_url + rename_column :accounts, :username, :cloud_servers_username + rename_column :accounts, :api_key, :cloud_servers_api_key + end + +end diff --git a/db/migrate/029_update_servers5.rb b/db/migrate/029_update_servers5.rb new file mode 100644 index 0000000..e1a343a --- /dev/null +++ b/db/migrate/029_update_servers5.rb @@ -0,0 +1,15 @@ +class UpdateServers5 < ActiveRecord::Migration + + def self.up + change_column :servers, :cloud_server_id_number, :string + change_column :servers, :image_id, :string + change_column :servers, :flavor_id, :string + end + + def self.down + change_column :servers, :cloud_server_id_number, :integer + change_column :servers, :image_id, :integer + change_column :servers, :flavor_id, :integer + end + +end diff --git a/db/schema.rb b/db/schema.rb index b80f389..303c1ff 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,14 +11,16 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 27) do +ActiveRecord::Schema.define(:version => 29) do create_table "accounts", :force => true do |t| - t.string "cloud_servers_username" - t.string "cloud_servers_api_key" + t.string "username" + t.string "api_key" t.datetime "created_at" t.datetime "updated_at" t.integer "user_id" + t.string "connection_type", :default => "rackspace" + t.string "auth_url", :default => "" end add_index "accounts", ["user_id"], :name => "index_accounts_on_user_id" @@ -85,9 +87,9 @@ t.string "description", :null => false t.string "external_ip_addr" t.string "internal_ip_addr" - t.integer "cloud_server_id_number" - t.integer "flavor_id", :null => false - t.integer "image_id", :null => false + t.string "cloud_server_id_number" + t.string "flavor_id", :null => false + t.string "image_id", :null => false t.integer "server_group_id", :null => false t.boolean "openvpn_server", :default => false, :null => false t.string "status", :default => "Pending", :null => false diff --git a/lib/cloud_servers_util.rb b/lib/cloud_servers_util.rb deleted file mode 100644 index 639f7f3..0000000 --- a/lib/cloud_servers_util.rb +++ /dev/null @@ -1,68 +0,0 @@ -require 'rubygems' -require 'cloudservers' - -# wrapper around all Cloud Server API calls -class CloudServersUtil - - @cs_conn=nil - - def initialize(username, api_key) - @cs_conn = CloudServers::Connection.new(:username => username, :api_key => api_key, :retry_auth => true) - end - - # Create a cloud server instance with root ssh access - # This function requires that a $HOME/.ssh/id_rsa.pub key - # exist for the process running this web application - def create_cloud_server(name, image_id, flavor_id, personalities={}) - - @cs_conn.create_server( - :name => name, - :imageId => image_id, - :flavorId => flavor_id, - :personality => personalities) - - end - - def find_server(id) - @cs_conn.server(id) - end - - def destroy_server(id) - server=@cs_conn.server(id) - server.delete! - end - - def reboot_server(id) - server=@cs_conn.server(id) - server.reboot! - end - - def all_servers - - if block_given? then - @cs_conn.servers.each do |server| - yield server - end - else - @cs_conn.servers - end - - end - - def all_images - - if block_given? then - @cs_conn.images.each do |image| - yield image - end - else - @cs_conn.images - end - - end - - def account_limits - @cs_conn.limits - end - -end diff --git a/lib/openstack_connection.rb b/lib/openstack_connection.rb new file mode 100644 index 0000000..81e2c2e --- /dev/null +++ b/lib/openstack_connection.rb @@ -0,0 +1,91 @@ +require 'rubygems' +require 'openstack/compute' + +# wrapper around all OpenStack Compute API calls +class OpenstackConnection + + @os_conn=nil + + def initialize(username, api_key, auth_url) + @os_conn = OpenStack::Compute::Connection.new(:username => username, :api_key => api_key, :auth_url => auth_url, :retry_auth => true) + end + + #return an array containing the server id and admin password + def create_server(name, image_ref, flavor_ref, personalities={}) + server = @os_conn.create_server( + :name => name, + :imageRef => image_ref, + :flavorRef => flavor_ref, + :personality => personalities) + [server.id, server.adminPass] + end + + # returns a hash containing detailed server info + def get_server(id) + server = @os_conn.server(id) + server_data = { + :id => server.id, + :progress => server.progress, + :status => server.status + } + begin + if server.addresses.size > 0 and server.addresses[:public] and server.addresses[:private] then + + pubs = server.addresses[:public].reject {|addr| addr.version != 4} + privs = server.addresses[:private].reject {|addr| addr.version != 4} + + server_data.store(:public_ip, pubs[0].address) + server_data.store(:private_ip, privs[0].address) + end + rescue Exception => e + #puts "Failed to get address info: " + e.message + end + server_data + end + + def update_server(id, data) + server = @os_conn.server(id) + server.update(data) + end + + def destroy_server(id) + server = @os_conn.server(id) + server.delete! + end + + def reboot_server(id) + server = @os_conn.server(id) + server.reboot! + end + + # returns an array of :id, :name hashes + def all_servers + + if block_given? then + @os_conn.servers.each do |server| + yield server + end + else + @os_conn.servers + end + + end + + # returns an array of :id, :name hashes + def all_images + + if block_given? then + @os_conn.images.each do |image| + yield image + end + else + @os_conn.images + end + + end + + def account_limits + @os_conn.limits + end + +end diff --git a/lib/rackspace_connection.rb b/lib/rackspace_connection.rb new file mode 100644 index 0000000..3a88c38 --- /dev/null +++ b/lib/rackspace_connection.rb @@ -0,0 +1,84 @@ +require 'rubygems' +require 'cloudservers' + +# wrapper around all Rackspace Cloud Servers API calls +class RackspaceConnection + + @cs_conn=nil + + def initialize(username, api_key, auth_url) + if auth_url.blank? then + @cs_conn = CloudServers::Connection.new(:username => username, :api_key => api_key, :retry_auth => true) + else + @cs_conn = CloudServers::Connection.new(:username => username, :api_key => api_key, :retry_auth => true, :auth_url => auth_url) + end + end + + #return an array containing the server id and admin password + def create_server(name, image_id, flavor_id, personalities={}) + server = @cs_conn.create_server( + :name => name, + :imageId => image_id.to_i, + :flavorId => flavor_id.to_i, + :personality => personalities) + [server.id, server.adminPass] + end + + # returns a hash containing detailed server info + def get_server(id) + server = @cs_conn.server(id.to_i) + { + :id => server.id, + :progress => server.progress, + :status => server.status, + :public_ip => server.addresses[:public][0], + :private_ip => server.addresses[:private][0] + } + end + + def update_server(id, data) + server = @cs_conn.server(id.to_i) + server.update(data) + end + + def destroy_server(id) + server = @cs_conn.server(id.to_i) + server.delete! + end + + def reboot_server(id) + server = @cs_conn.server(id.to_i) + server.reboot! + end + + # returns an array of :id, :name hashes + def all_servers + + if block_given? then + @cs_conn.servers.each do |server| + yield server + end + else + @cs_conn.servers + end + + end + + # returns an array of :id, :name hashes + def all_images + + if block_given? then + @cs_conn.images.each do |image| + yield image + end + else + @cs_conn.images + end + + end + + def account_limits + @cs_conn.limits + end + +end diff --git a/script/about b/script/about deleted file mode 100755 index 1eeb6eb..0000000 --- a/script/about +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env ruby -require File.expand_path('../../config/boot', __FILE__) -$LOAD_PATH.unshift "#{RAILTIES_PATH}/builtin/rails_info" -require 'commands/about' diff --git a/script/console b/script/console deleted file mode 100755 index 235a1f2..0000000 --- a/script/console +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.expand_path('../../config/boot', __FILE__) -require 'commands/console' diff --git a/script/dbconsole b/script/dbconsole deleted file mode 100755 index 83c8436..0000000 --- a/script/dbconsole +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.expand_path('../../config/boot', __FILE__) -require 'commands/dbconsole' diff --git a/script/destroy b/script/destroy deleted file mode 100755 index 88d295f..0000000 --- a/script/destroy +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.expand_path('../../config/boot', __FILE__) -require 'commands/destroy' diff --git a/script/generate b/script/generate deleted file mode 100755 index 62a8a4c..0000000 --- a/script/generate +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.expand_path('../../config/boot', __FILE__) -require 'commands/generate' diff --git a/script/performance/benchmarker b/script/performance/benchmarker deleted file mode 100755 index 3bff809..0000000 --- a/script/performance/benchmarker +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.expand_path('../../../config/boot', __FILE__) -require 'commands/performance/benchmarker' diff --git a/script/performance/profiler b/script/performance/profiler deleted file mode 100755 index 0764057..0000000 --- a/script/performance/profiler +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.expand_path('../../../config/boot', __FILE__) -require 'commands/performance/profiler' diff --git a/script/plugin b/script/plugin deleted file mode 100755 index b82201f..0000000 --- a/script/plugin +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.expand_path('../../config/boot', __FILE__) -require 'commands/plugin' diff --git a/script/runner b/script/runner deleted file mode 100755 index be4c5d4..0000000 --- a/script/runner +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.expand_path('../../config/boot', __FILE__) -require 'commands/runner' diff --git a/script/server b/script/server deleted file mode 100755 index b9fcb71..0000000 --- a/script/server +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.expand_path('../../config/boot', __FILE__) -require 'commands/server' diff --git a/test/fixtures/accounts.yml b/test/fixtures/accounts.yml index 92d9487..6bd0f46 100644 --- a/test/fixtures/accounts.yml +++ b/test/fixtures/accounts.yml @@ -1,16 +1,19 @@ # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html admin_account: - cloud_servers_username: blahblah - cloud_servers_api_key: ABABABABABA + username: blahblah + api_key: ABABABABABA user: admin + connection_type: rackspace bob_account: - cloud_servers_username: blahblah - cloud_servers_api_key: ABABABABABA + username: blahblah + api_key: ABABABABABA user: bob + connection_type: rackspace jim_account: - cloud_servers_username: blahblah - cloud_servers_api_key: ABABABABABA + username: blahblah + api_key: ABABABABABA user: jim + connection_type: rackspace diff --git a/test/functional/accounts_controller_test.rb b/test/functional/accounts_controller_test.rb index b3cff40..981ad39 100644 --- a/test/functional/accounts_controller_test.rb +++ b/test/functional/accounts_controller_test.rb @@ -10,26 +10,26 @@ class AccountsControllerTest < ActionController::TestCase test "should create account" do login_as(:bob) assert_difference('Account.count') do - post :create, :account => {:cloud_servers_username => "blah", :cloud_servers_api_key => "ABABABABABAB"}, :format => :json + post :create, :account => {:username => "blah", :api_key => "ABABABABABAB"}, :format => :json end assert_response :success end test "admin update account" do login_as(:admin) - put :update, :id => accounts(:jim_account).to_param, :account => {:cloud_servers_username => "blah", :cloud_servers_api_key => "ABABABABABAB"} + put :update, :id => accounts(:jim_account).to_param, :account => {:username => "blah", :api_key => "ABABABABABAB"} assert_redirected_to account_path(assigns(:account)) end test "user update account" do login_as(:jim) - put :update, :id => accounts(:jim_account).to_param, :account => {:cloud_servers_username => "blah", :cloud_servers_api_key => "ABABABABABAB"} + put :update, :id => accounts(:jim_account).to_param, :account => {:username => "blah", :api_key => "ABABABABABAB"} assert_redirected_to account_path(assigns(:account)) end test "user should not update another users account" do login_as(:bob) - put :update, :id => accounts(:jim_account).to_param, :account => {:cloud_servers_username => "blah", :cloud_servers_api_key => "ABABABABABAB"} + put :update, :id => accounts(:jim_account).to_param, :account => {:username => "blah", :api_key => "ABABABABABAB"} assert_response 401 end diff --git a/test/functional/server_groups_controller_test.rb b/test/functional/server_groups_controller_test.rb index 31a912d..e2669cc 100644 --- a/test/functional/server_groups_controller_test.rb +++ b/test/functional/server_groups_controller_test.rb @@ -35,7 +35,7 @@ class ServerGroupsControllerTest < ActionController::TestCase end =end - test "should create server_group with server" do + test "should create server_group with server and client" do http_basic_authorize assert_difference('Client.count') do assert_difference('Server.count') do @@ -48,13 +48,30 @@ class ServerGroupsControllerTest < ActionController::TestCase end + test "should create server_group with image uuid" do + + image_id = '11b2a5bf-590c-4dd4-931f-a65751a4db0e' + + http_basic_authorize + assert_difference('Client.count') do + assert_difference('Server.count') do + assert_difference('ServerGroup.count') do + post :create, :server_group => { :name => "test1", :owner_name => "dan.prince", :domain_name => "test.rsapps.net", :description => "test1", :vpn_network => "172.19.0.0", :vpn_subnet => "255.255.128.0", :servers_attributes => {"0" => { :name => "test1", :description => "test description", :flavor_id => "2", :image_id => image_id }}, :client_attributes => {"0" => {:name => "test2", :description => "blah blah"}} } + end + end + end + assert_response :success + + assert_equal 1, Server.count(:conditions => ["image_id = ?", image_id]) + + end + test "should create server_group" do http_basic_authorize assert_difference('ServerGroup.count') do post :create, :server_group => { :name => "test1", :owner_name => "dan.prince", :domain_name => "test.rsapps.net", :description => "test1", :vpn_network => "172.19.0.0", :vpn_subnet => "255.255.128.0" } end assert_response :success - #assert_redirected_to server_group_path(assigns(:server_group)) end @@ -92,6 +109,34 @@ class ServerGroupsControllerTest < ActionController::TestCase end + test "should create server_group via XML request with image uuid" do + + image_id = '22b2a5bf-590c-4dd4-931f-a65751a4db0c' + + http_basic_authorize + assert_difference('SshPublicKey.count') do + assert_difference('Client.count') do + assert_difference('Server.count') do + assert_difference('ServerGroup.count') do + + @request.env['RAW_POST_DATA'] = get_xml_request(image_id) + + @request.accept = 'text/xml' + response=post :create + @request.env.delete('RAW_POST_DATA') + + end + end + end + end + + assert_response :success + + server=Server.find(:first, :conditions => ["image_id = ?", image_id]) + assert_equal server.id, AsyncExec.jobs[CreateCloudServer][0] + + end + test "should not create server_group w/ Windows VPN server via XML request" do http_basic_authorize @@ -281,8 +326,8 @@ def get_xml_request(image_id) test1 test1 - 1 - #{image_id} + 1 + #{image_id} true #{Base64.encode64("echo hello > /tmp/test.txt")} diff --git a/test/functional/users_controller_test.rb b/test/functional/users_controller_test.rb index 575f271..73e6dc2 100644 --- a/test/functional/users_controller_test.rb +++ b/test/functional/users_controller_test.rb @@ -32,18 +32,18 @@ class UsersControllerTest < ActionController::TestCase test "should create user with account" do assert_difference('User.count') do - post :create, :user => {:username => "test1", :first_name => "Mr.", :last_name => "Big", :password => "test123", :account_attributes => {:cloud_servers_username => 'blah123', :cloud_servers_api_key => 'AABBCCDD'} } + post :create, :user => {:username => "test1", :first_name => "Mr.", :last_name => "Big", :password => "test123", :account_attributes => {:username => 'blah123', :api_key => 'AABBCCDD'} } end user=User.find(:first, :conditions => ["username = ?", "test1"]) - assert_equal "blah123", user.account.cloud_servers_username, "Cloud Servers username was not set." - assert_equal "AABBCCDD", user.account.cloud_servers_api_key, "Cloud Servers api key was not set." + assert_equal "blah123", user.account.username, "Cloud Servers username was not set." + assert_equal "AABBCCDD", user.account.api_key, "Cloud Servers api key was not set." assert_redirected_to user_path(assigns(:user)) end test "should not create user with invalid account" do assert_no_difference('User.count') do ENV['CLOUD_SERVERS_UTIL_INIT_MOCK_FAIL']="true" - post :create, :user => {:username => "test1", :first_name => "Mr.", :last_name => "Big", :password => "test123", :account_attributes => {:cloud_servers_username => 'blah123', :cloud_servers_api_key => 'AABBCCDD'} } + post :create, :user => {:username => "test1", :first_name => "Mr.", :last_name => "Big", :password => "test123", :account_attributes => {:username => 'blah123', :api_key => 'AABBCCDD'} } end end diff --git a/test/mocks/test/cloud_servers_util.rb b/test/mocks/test/cloud_servers_util.rb deleted file mode 100644 index d7124ca..0000000 --- a/test/mocks/test/cloud_servers_util.rb +++ /dev/null @@ -1,50 +0,0 @@ -class CloudServersUtil - - class TestCloudServer - attr_accessor :name, :imageId, :flavorId, :hostId, :status, :progress, :addresses, :metadata, :personality, :adminPass - end - - def initialize(username, api_key) - if ENV['CLOUD_SERVERS_UTIL_INIT_MOCK_FAIL'] then - ENV.delete('CLOUD_SERVERS_UTIL_INIT_MOCK_FAIL') - raise "Invalid account specified" - else - return true - end - end - - def create_cloud_server(name, image_id, flavor_id, personalities={}) - server=TestCloudServer.new - server.name = name - server.imageId = image_id - server.flavorId = flavor_id - server.progress = 100 - return server - end - - def find_server(id) - server=TestCloudServer.new - server.name = name - server.imageId = image_id - server.flavorId = flavor_id - return server - end - - def destroy_server(id) - return true - end - - def reboot_server(id) - return true - end - - def all_servers - return [] - end - - def account_limits - return %{{"absolute":{"maxIPGroups":25,"maxIPGroupMembers":25,"maxTotalRAMSize":51200},"rate":[{"remaining":10,"URI":"*","unit":"MINUTE","resetTime":1287082645,"value":10,"regex":".*","verb":"PUT"},{"remaining":3,"URI":"*changes-since*","unit":"MINUTE","resetTime":1287082645,"value":3,"regex":"changes-since","verb":"GET"},{"remaining":600,"URI":"*","unit":"MINUTE","resetTime":1287082645,"value":600,"regex":".*","verb":"DELETE"},{"remaining":58,"URI":"/servers*","unit":"HOUR","resetTime":1287083964,"value":60,"regex":"^/servers","verb":"POST"}]} -} - end - -end diff --git a/test/mocks/test/rackspace_connection.rb b/test/mocks/test/rackspace_connection.rb new file mode 100644 index 0000000..826cdc5 --- /dev/null +++ b/test/mocks/test/rackspace_connection.rb @@ -0,0 +1,61 @@ +class RackspaceConnection + + class TestCloudServer + attr_accessor :id, :name, :imageId, :flavorId, :hostId, :status, :progress, :addresses, :metadata, :personality, :adminPass + end + + def initialize(username, api_key, auth_url) + if ENV['CLOUD_SERVERS_UTIL_INIT_MOCK_FAIL'] then + ENV.delete('CLOUD_SERVERS_UTIL_INIT_MOCK_FAIL') + raise "Invalid account specified" + else + return true + end + end + + def create_cloud_server(name, image_id, flavor_id, personalities={}) + server=TestCloudServer.new + server.name = name + server.imageId = image_id + server.flavorId = flavor_id + server.progress = 100 + return server.id + end + + def server_details(id) + server=TestCloudServer.new + server.name = name + server.imageId = image_id + server.flavorId = flavor_id + return { + :id => server.id, + :progress => server.progress, + :status => server.status, + :public_ip => server.addresses[:public][0], + :private_ip => server.addresses[:public][0], + :admin_password => server.adminPass + } + end + + def destroy_server(id) + return true + end + + def reboot_server(id) + return true + end + + def all_servers + return [] + end + + def all_images + return [] + end + + def account_limits + return %{{"absolute":{"maxIPGroups":25,"maxIPGroupMembers":25,"maxTotalRAMSize":51200},"rate":[{"remaining":10,"URI":"*","unit":"MINUTE","resetTime":1287082645,"value":10,"regex":".*","verb":"PUT"},{"remaining":3,"URI":"*changes-since*","unit":"MINUTE","resetTime":1287082645,"value":3,"regex":"changes-since","verb":"GET"},{"remaining":600,"URI":"*","unit":"MINUTE","resetTime":1287082645,"value":600,"regex":".*","verb":"DELETE"},{"remaining":58,"URI":"/servers*","unit":"HOUR","resetTime":1287083964,"value":60,"regex":"^/servers","verb":"POST"}]} +} + end + +end diff --git a/test/test_helper.rb b/test/test_helper.rb index 7ee7dc5..b98a3ba 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -2,7 +2,7 @@ require File.expand_path('../../config/environment', __FILE__) require 'rails/test_help' require File.expand_path(File.dirname(__FILE__) + "/mocks/test/async_exec") -require File.expand_path(File.dirname(__FILE__) + "/mocks/test/cloud_servers_util") +require File.expand_path(File.dirname(__FILE__) + "/mocks/test/rackspace_connection") class ActiveSupport::TestCase # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order. diff --git a/test/unit/account_test.rb b/test/unit/account_test.rb index ab0e68f..ec0d56e 100644 --- a/test/unit/account_test.rb +++ b/test/unit/account_test.rb @@ -16,8 +16,8 @@ class AccountTest < ActiveSupport::TestCase test "create account" do account=Account.new( - :cloud_servers_username => "blah", - :cloud_servers_api_key => "ABABABABABAB" + :username => "blah", + :api_key => "ABABABABABAB" ) assert account.valid?, "Account should be valid." assert account.save, "Account should have been saved." diff --git a/test/unit/user_test.rb b/test/unit/user_test.rb index c46fa5d..5971261 100644 --- a/test/unit/user_test.rb +++ b/test/unit/user_test.rb @@ -11,7 +11,7 @@ class ServerTest < ActiveSupport::TestCase :first_name => "Mr.", :last_name => "Big", :password => "test123", - :account_attributes => {:cloud_servers_username => "test", :cloud_servers_api_key => "AABBCCDD"} + :account_attributes => {:username => "test", :api_key => "AABBCCDD"} ) assert user.valid?, "User should be valid." assert user.save, "User should have been saved." @@ -26,7 +26,7 @@ class ServerTest < ActiveSupport::TestCase :first_name => "Mr.", :last_name => "Big", :password => "test123", - :account_attributes => {:cloud_servers_username => "test", :cloud_servers_api_key => "AABBCCDD"} + :account_attributes => {:username => "test", :api_key => "AABBCCDD"} ) assert_equal false, user.valid?, "User should not be valid." @@ -39,7 +39,7 @@ class ServerTest < ActiveSupport::TestCase :first_name => "Mr.", :last_name => "Big", :password => "test123", - :account_attributes => {:cloud_servers_username => "test", :cloud_servers_api_key => "AABBCCDD"} + :account_attributes => {:username => "test", :api_key => "AABBCCDD"} ) assert_equal false, user.valid?, "User should not be valid." assert_equal false, user.save, "User should not save." @@ -53,7 +53,7 @@ class ServerTest < ActiveSupport::TestCase :first_name => "Mr.", :last_name => "Big", :password => "test123", - :account_attributes => {:cloud_servers_username => "test", :cloud_servers_api_key => "AABBCCDD"} + :account_attributes => {:username => "test", :api_key => "AABBCCDD"} ) assert_equal false, user.valid?, "User should not be valid." assert_equal false, user.save, "User should not save." @@ -67,7 +67,7 @@ class ServerTest < ActiveSupport::TestCase :first_name => "Test", :last_name => "SshKey", :password => "test123", - :account_attributes => {:cloud_servers_username => "test", :cloud_servers_api_key => "AABBCCDD"} + :account_attributes => {:username => "test", :api_key => "AABBCCDD"} ) assert_equal true, user.save, "User should save."