rest_connection 0.0.23 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. data/README.rdoc +3 -3
  2. data/Rakefile +5 -4
  3. data/VERSION +1 -1
  4. data/git_hooks/post-commit.disabled +4 -0
  5. data/git_hooks/post-merge.disabled +4 -0
  6. data/git_hooks/pre-commit +53 -0
  7. data/lib/rest_connection.rb +75 -12
  8. data/lib/rest_connection/patches.rb +106 -0
  9. data/lib/rest_connection/rightscale/account.rb +27 -0
  10. data/lib/rest_connection/rightscale/alert_spec.rb +2 -2
  11. data/lib/rest_connection/rightscale/audit_entry.rb +5 -5
  12. data/lib/rest_connection/rightscale/child_account.rb +27 -0
  13. data/lib/rest_connection/rightscale/cloud.rb +26 -0
  14. data/lib/rest_connection/rightscale/credential.rb +1 -1
  15. data/lib/rest_connection/rightscale/deployment.rb +33 -7
  16. data/lib/rest_connection/rightscale/ec2_ebs_snapshot.rb +3 -1
  17. data/lib/rest_connection/rightscale/ec2_ebs_volume.rb +3 -1
  18. data/lib/rest_connection/rightscale/ec2_elastic_ip.rb +1 -1
  19. data/lib/rest_connection/rightscale/ec2_security_group.rb +2 -2
  20. data/lib/rest_connection/rightscale/ec2_server_array.rb +7 -5
  21. data/lib/rest_connection/rightscale/ec2_ssh_key.rb +11 -2
  22. data/lib/rest_connection/rightscale/ec2_ssh_key_internal.rb +2 -2
  23. data/lib/rest_connection/rightscale/executable.rb +4 -4
  24. data/lib/rest_connection/rightscale/instance.rb +5 -3
  25. data/lib/rest_connection/rightscale/instance_type.rb +31 -0
  26. data/lib/rest_connection/rightscale/macro.rb +19 -0
  27. data/lib/rest_connection/rightscale/mc_datacenter.rb +53 -0
  28. data/lib/rest_connection/rightscale/mc_deployment.rb +60 -0
  29. data/lib/rest_connection/rightscale/mc_image.rb +42 -0
  30. data/lib/rest_connection/rightscale/mc_instance.rb +170 -0
  31. data/lib/rest_connection/rightscale/mc_instance_type.rb +47 -0
  32. data/lib/rest_connection/rightscale/mc_multi_cloud_image.rb +58 -0
  33. data/lib/rest_connection/rightscale/mc_multi_cloud_image_setting.rb +46 -0
  34. data/lib/rest_connection/rightscale/mc_security_group.rb +42 -0
  35. data/lib/rest_connection/rightscale/mc_server.rb +250 -6
  36. data/lib/rest_connection/rightscale/mc_server_array.rb +44 -0
  37. data/lib/rest_connection/rightscale/mc_server_template.rb +53 -0
  38. data/lib/rest_connection/rightscale/mc_ssh_key.rb +50 -0
  39. data/lib/rest_connection/rightscale/mc_tag.rb +69 -0
  40. data/lib/rest_connection/rightscale/mc_volume.rb +53 -0
  41. data/lib/rest_connection/rightscale/mc_volume_attachment.rb +48 -0
  42. data/lib/rest_connection/rightscale/mc_volume_snapshot.rb +55 -0
  43. data/lib/rest_connection/rightscale/mc_volume_type.rb +47 -0
  44. data/lib/rest_connection/rightscale/monitoring_metric.rb +35 -0
  45. data/lib/rest_connection/rightscale/multi_cloud_image.rb +16 -2
  46. data/lib/rest_connection/rightscale/multi_cloud_image_cloud_setting_internal.rb +2 -2
  47. data/lib/rest_connection/rightscale/multi_cloud_image_internal.rb +25 -1
  48. data/lib/rest_connection/rightscale/permission.rb +27 -0
  49. data/lib/rest_connection/rightscale/right_script.rb +4 -4
  50. data/lib/rest_connection/rightscale/right_script_internal.rb +4 -4
  51. data/lib/rest_connection/rightscale/rightscale_api_base.rb +69 -20
  52. data/lib/rest_connection/rightscale/rightscale_api_gateway.rb +198 -13
  53. data/lib/rest_connection/rightscale/rightscale_api_internal.rb +3 -3
  54. data/lib/rest_connection/rightscale/rightscale_api_mc_input.rb +32 -0
  55. data/lib/rest_connection/rightscale/rightscale_api_mc_taggable.rb +57 -0
  56. data/lib/rest_connection/rightscale/rightscale_api_resources.rb +32 -2
  57. data/lib/rest_connection/rightscale/rightscale_api_taggable.rb +116 -0
  58. data/lib/rest_connection/rightscale/rs_internal.rb +6 -6
  59. data/lib/rest_connection/rightscale/s3_bucket.rb +36 -0
  60. data/lib/rest_connection/rightscale/server.rb +186 -37
  61. data/lib/rest_connection/rightscale/server_interface.rb +264 -0
  62. data/lib/rest_connection/rightscale/server_internal.rb +3 -3
  63. data/lib/rest_connection/rightscale/server_template.rb +15 -4
  64. data/lib/rest_connection/rightscale/server_template_internal.rb +4 -4
  65. data/lib/rest_connection/rightscale/status.rb +2 -2
  66. data/lib/rest_connection/rightscale/tag.rb +2 -2
  67. data/lib/rest_connection/rightscale/task.rb +47 -0
  68. data/lib/rest_connection/rightscale/user.rb +27 -0
  69. data/lib/rest_connection/ssh_hax.rb +42 -40
  70. data/rest_connection.gemspec +5 -5
  71. data/spec/mcserver_spec.rb +17 -0
  72. data/spec/multi.rb +16 -0
  73. metadata +65 -12
@@ -4,7 +4,7 @@ module RightScale
4
4
  def connection
5
5
  @@little_brother_connection ||= RestConnection::Connection.new
6
6
  settings = @@little_brother_connection.settings
7
- settings[:common_headers]["X_API_VERSION"] = "0.1"
7
+ settings[:common_headers]["X_API_VERSION"] = "0.1"
8
8
  settings[:api_href] = settings[:api_url]
9
9
  settings[:extension] = ".js"
10
10
  @@little_brother_connection
@@ -15,7 +15,7 @@ module RightScale
15
15
  def connection
16
16
  @@little_brother_connection ||= RestConnection::Connection.new
17
17
  settings = @@little_brother_connection.settings
18
- settings[:common_headers]["X_API_VERSION"] = "0.1"
18
+ settings[:common_headers]["X_API_VERSION"] = "0.1"
19
19
  settings[:api_href] = settings[:api_url]
20
20
  settings[:extension] = ".js"
21
21
  @@little_brother_connection
@@ -23,4 +23,4 @@ module RightScale
23
23
  end
24
24
  end
25
25
  end
26
-
26
+
@@ -0,0 +1,32 @@
1
+ # This file is part of RestConnection
2
+ #
3
+ # RestConnection is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # RestConnection is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with RestConnection. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ #
17
+ # You must have Beta v1.5 API access to use these internal API calls.
18
+ #
19
+ module RightScale
20
+ module Api
21
+ module McInput
22
+ def get_inputs
23
+ self["inputs"] = connection.get(self.href + "/inputs")
24
+ end
25
+
26
+ def show
27
+ inst_href = URI.parse(self.href)
28
+ @params.merge! connection.get(inst_href.path, 'view' => "inputs")
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,57 @@
1
+ # This file is part of RestConnection
2
+ #
3
+ # RestConnection is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # RestConnection is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with RestConnection. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ #
17
+ # You must have Beta v1.5 API access to use these internal API calls.
18
+ #
19
+ module RightScale
20
+ module Api
21
+ module McTaggable
22
+ include RightScale::Api::Taggable
23
+ def add_tags(*args)
24
+ return false if args.empty?
25
+ McTag.set(self.href, args.uniq)
26
+ self.tags(true)
27
+ end
28
+
29
+ def remove_tags(*args)
30
+ return false if args.empty?
31
+ McTag.unset(self.href, args.uniq)
32
+ @params["tags"] -= args
33
+ self.tags(true)
34
+ end
35
+
36
+ def tags(reload=false)
37
+ @params["tags"] ||= []
38
+ @params["tags"].map! { |item| item.is_a?(Hash) ? item["name"] : item }
39
+ @params["tags"].deep_merge!(McTag.search_by_href(self.href).first["tags"].map { |hsh| hsh["name"] }) if reload or @params["tags"].empty?
40
+ @params["tags"]
41
+ end
42
+ end
43
+
44
+ module McTaggableExtend
45
+ def find_by_tags(*args)
46
+ a = Array.new
47
+ search = McTag.search(self.resource_plural_name, args.uniq).first
48
+ if search
49
+ search["links"].each do |hash|
50
+ a << self.find(hash["href"])
51
+ end
52
+ end
53
+ return a
54
+ end
55
+ end
56
+ end
57
+ end
@@ -1,4 +1,4 @@
1
- # This file is part of RestConnection
1
+ # This file is part of RestConnection
2
2
  #
3
3
  # RestConnection is free software: you can redistribute it and/or modify
4
4
  # it under the terms of the GNU General Public License as published by
@@ -17,6 +17,9 @@
17
17
  require 'rest_connection/rightscale/rightscale_api_base'
18
18
  require 'rest_connection/rightscale/rightscale_api_internal'
19
19
  require 'rest_connection/rightscale/rightscale_api_gateway'
20
+ require 'rest_connection/rightscale/rightscale_api_taggable'
21
+ require 'rest_connection/rightscale/rightscale_api_mc_taggable'
22
+ require 'rest_connection/rightscale/rightscale_api_mc_input'
20
23
  require 'rest_connection/rightscale/executable'
21
24
  require 'rest_connection/rightscale/server'
22
25
  require 'rest_connection/rightscale/deployment'
@@ -28,18 +31,45 @@ require 'rest_connection/rightscale/ec2_security_group'
28
31
  require 'rest_connection/rightscale/ec2_ssh_key'
29
32
  require 'rest_connection/rightscale/multi_cloud_image'
30
33
  require 'rest_connection/rightscale/tag'
34
+ require 'rest_connection/rightscale/mc_tag'
35
+ require 'rest_connection/rightscale/task'
31
36
  require 'rest_connection/rightscale/rs_internal'
32
37
  require 'rest_connection/rightscale/audit_entry'
33
38
  require 'rest_connection/rightscale/alert_spec'
34
39
  require 'rest_connection/rightscale/ec2_ebs_volume'
35
40
  require 'rest_connection/rightscale/ec2_ebs_snapshot'
41
+ require 'rest_connection/rightscale/mc_volume_attachment'
42
+ require 'rest_connection/rightscale/mc_volume'
43
+ require 'rest_connection/rightscale/mc_volume_snapshot'
44
+ require 'rest_connection/rightscale/mc_volume_type'
36
45
  require 'rest_connection/rightscale/server_internal'
37
46
  require 'rest_connection/rightscale/mc_server'
47
+ require 'rest_connection/rightscale/server_interface'
48
+ require 'rest_connection/rightscale/mc_instance'
49
+ require 'rest_connection/rightscale/monitoring_metric'
50
+ require 'rest_connection/rightscale/mc_multi_cloud_image_setting'
51
+ require 'rest_connection/rightscale/mc_multi_cloud_image'
52
+ require 'rest_connection/rightscale/mc_server_template'
38
53
  require 'rest_connection/rightscale/ec2_ssh_key_internal'
39
54
  require 'rest_connection/rightscale/server_template_internal'
40
55
  require 'rest_connection/rightscale/right_script_internal'
41
- require 'rest_connection/rightscale/multi_cloud_image_internal'
42
56
  require 'rest_connection/rightscale/multi_cloud_image_cloud_setting_internal'
57
+ require 'rest_connection/rightscale/multi_cloud_image_internal'
43
58
  require 'rest_connection/rightscale/ec2_server_array'
59
+ require 'rest_connection/rightscale/mc_server_array'
60
+ require 'rest_connection/rightscale/mc_security_group'
61
+ require 'rest_connection/rightscale/mc_deployment'
62
+ require 'rest_connection/rightscale/mc_datacenter'
63
+ require 'rest_connection/rightscale/mc_ssh_key'
44
64
  require 'rest_connection/rightscale/ec2_elastic_ip'
45
65
  require 'rest_connection/rightscale/credential'
66
+ require 'rest_connection/rightscale/cloud'
67
+ require 'rest_connection/rightscale/instance_type'
68
+ require 'rest_connection/rightscale/mc_instance_type'
69
+ require 'rest_connection/rightscale/mc_image'
70
+ require 'rest_connection/rightscale/macro'
71
+ require 'rest_connection/rightscale/s3_bucket'
72
+ require 'rest_connection/rightscale/account'
73
+ require 'rest_connection/rightscale/child_account'
74
+ require 'rest_connection/rightscale/permission'
75
+ require 'rest_connection/rightscale/user'
@@ -0,0 +1,116 @@
1
+ # This file is part of RestConnection
2
+ #
3
+ # RestConnection is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # RestConnection is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with RestConnection. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ module RightScale
17
+ module Api
18
+ module Taggable
19
+ def add_tags(*args)
20
+ return false if args.empty?
21
+ Tag.set(self.href, args.uniq)
22
+ self.tags(true)
23
+ end
24
+
25
+ def remove_tags(*args)
26
+ return false if args.empty?
27
+ Tag.unset(self.href, args.uniq)
28
+ @params["tags"] -= args
29
+ self.tags(true)
30
+ end
31
+
32
+ def tags(reload=false)
33
+ @params["tags"] ||= []
34
+ @params["tags"].map! { |item| item.is_a?(Hash) ? item["name"] : item }
35
+ @params["tags"].deep_merge!(Tag.search_by_href(self.href).map { |hsh| hsh["name"] }) if reload or @params["tags"].empty?
36
+ @params["tags"]
37
+ end
38
+
39
+ def remove_info_tags(*tag_keys)
40
+ remove_tags_by_namespace("info", *tag_keys)
41
+ end
42
+
43
+ def set_info_tags(hsh={})
44
+ set_tags_by_namespace("info", hsh)
45
+ end
46
+
47
+ def get_info_tags(*tag_keys)
48
+ tags = get_tags_by_namespace("info")
49
+ tags.each { |resource,hsh|
50
+ hsh.reject! { |key,value|
51
+ rej = false
52
+ rej = !tag_keys.include?(key) unless tag_keys.empty?
53
+ rej
54
+ }
55
+ }
56
+ return tags
57
+ end
58
+
59
+ def remove_tags_by_namespace(namespace, *tag_keys)
60
+ tags_to_unset = []
61
+ tags = get_tags_by_namespace(namespace)
62
+ tags.each { |res,hsh|
63
+ hsh.each { |k,v|
64
+ tags_to_unset << "#{namespace}:#{k}=#{v}" if tag_keys.include?(k)
65
+ }
66
+ }
67
+ self.remove_tags(*tags_to_unset)
68
+ end
69
+
70
+ def set_tags_by_namespace(namespace, hsh={})
71
+ keys_to_change = []
72
+ tags_to_set = []
73
+ hsh.each { |k,v| keys_to_change << k; tags_to_set << "#{namespace}:#{k}=#{v}" }
74
+ self.remove_tags_by_namespace(namespace, *keys_to_change)
75
+ self.add_tags(*tags_to_set)
76
+ end
77
+
78
+ def get_tags_by_namespace(namespace)
79
+ ret = {}
80
+ tags = {"self" => self.tags(true)}
81
+ tags.each { |res,ary|
82
+ ret[res] ||= {}
83
+ ary.each { |tag|
84
+ next unless tag.start_with?("#{namespace}:")
85
+ key = tag.split("=").first.split(":")[1..-1].join(":")
86
+ value = tag.split(":")[1..-1].join(":").split("=")[1..-1].join("=")
87
+ ret[res][key] = value
88
+ }
89
+ }
90
+ return ret
91
+ end
92
+
93
+ def set_tags_to(*args)
94
+ STDERR.puts "set_tags_to(...) is deprecated"
95
+ self.clear_tags("info")
96
+ self.add_tags(*(args.uniq))
97
+ end
98
+
99
+ def clear_tags(namespace = nil)
100
+ tag_ary = self.tags(true)
101
+ tag_ary = tag_ary.select { |tag| tag.start_with?("#{namespace}:") } if namespace
102
+ self.remove_tags(*tag_ary)
103
+ end
104
+ end
105
+
106
+ module TaggableExtend
107
+ def find_by_tags(*args)
108
+ a = Array.new
109
+ Tag.search(self.resource_singular_name, args.uniq).each do |object|
110
+ a << self.new(object)
111
+ end
112
+ return a
113
+ end
114
+ end
115
+ end
116
+ end
@@ -1,4 +1,4 @@
1
- # This file is part of RestConnection
1
+ # This file is part of RestConnection
2
2
  #
3
3
  # RestConnection is free software: you can redistribute it and/or modify
4
4
  # it under the terms of the GNU General Public License as published by
@@ -13,17 +13,17 @@
13
13
  # You should have received a copy of the GNU General Public License
14
14
  # along with RestConnection. If not, see <http://www.gnu.org/licenses/>.
15
15
 
16
- #
16
+ #
17
17
  # You must have special API access to use these internal API calls.
18
- #
19
- class RsInternal
18
+ #
19
+ class RsInternal
20
20
  include RightScale::Api::Base
21
21
  extend RightScale::Api::BaseExtend
22
22
 
23
23
  def connection
24
24
  @@little_brother_connection ||= RestConnection::Connection.new
25
25
  settings = @@little_brother_connection.settings
26
- settings[:common_headers]["X_API_VERSION"] = "0.1"
26
+ settings[:common_headers]["X_API_VERSION"] = "0.1"
27
27
  settings[:api_href] = settings[:api_url]
28
28
  settings[:extension] = ".js"
29
29
  @@little_brother_connection
@@ -32,7 +32,7 @@ class RsInternal
32
32
  def self.connection
33
33
  @@little_brother_connection ||= RestConnection::Connection.new
34
34
  settings = @@little_brother_connection.settings
35
- settings[:common_headers]["X_API_VERSION"] = "0.1"
35
+ settings[:common_headers]["X_API_VERSION"] = "0.1"
36
36
  settings[:api_href] = settings[:api_url]
37
37
  settings[:extension] = ".js"
38
38
  @@little_brother_connection
@@ -0,0 +1,36 @@
1
+ # This file is part of RestConnection
2
+ #
3
+ # RestConnection is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # RestConnection is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with RestConnection. If not, see <http://www.gnu.org/licenses/>.
15
+ #
16
+ class S3Bucket
17
+ include RightScale::Api::Base
18
+ extend RightScale::Api::BaseExtend
19
+
20
+ def self.resource_singular_name
21
+ "s3_bucket"
22
+ end
23
+
24
+ def self.resource_plural_name
25
+ "s3_buckets"
26
+ end
27
+
28
+ def resource_singular_name
29
+ "s3_bucket"
30
+ end
31
+
32
+ def resource_plural_name
33
+ "s3_buckets"
34
+ end
35
+
36
+ end
@@ -1,4 +1,4 @@
1
- # This file is part of RestConnection
1
+ # This file is part of RestConnection
2
2
  #
3
3
  # RestConnection is free software: you can redistribute it and/or modify
4
4
  # it under the terms of the GNU General Public License as published by
@@ -15,23 +15,27 @@
15
15
 
16
16
  require 'rest_connection/ssh_hax'
17
17
 
18
- class Server
18
+ class Server
19
19
  include RightScale::Api::Base
20
20
  extend RightScale::Api::BaseExtend
21
21
  include SshHax
22
+ include RightScale::Api::Taggable
23
+ extend RightScale::Api::TaggableExtend
22
24
 
23
25
  def self.create(opts)
24
26
  create_options = Hash.new
25
27
  create_options[self.resource_singular_name.to_sym] = opts
26
28
  create_options["cloud_id"] = opts[:cloud_id] if opts[:cloud_id]
27
- location = connection.post(self.resource_plural_name,create_options)
29
+ create_options[self.resource_singular_name.to_sym][:mci_href] = nil
30
+ create_options[self.resource_singular_name.to_sym][:inputs] = nil
31
+ location = connection.post(self.resource_plural_name,create_options)
28
32
  newrecord = self.new('href' => location)
29
33
  newrecord.reload
30
34
  newrecord.parameters #transform the parameters!
31
35
  newrecord
32
36
  end
33
37
 
34
- # The RightScale api returns the server parameters as a hash with "name" and "value".
38
+ # The RightScale api returns the server parameters as a hash with "name" and "value".
35
39
  # This must be transformed into a hash in case we want to PUT this back to the API.
36
40
  def transform_parameters(parameters)
37
41
  new_params_hash = {}
@@ -48,12 +52,13 @@ class Server
48
52
  if @params['parameters'].is_a?(Array)
49
53
  @params['parameters'] = transform_parameters(@params['parameters'])
50
54
  end
51
- @params['parameters']
55
+ @params['parameters'] ||= {}
52
56
  end
53
57
 
54
58
  # This is overriding the default save with one that can massage the parameters
55
59
  def save
56
60
  self.parameters #transform the parameters, if they're not already!!
61
+ self.tags #transform the tags, if they're not already!!
57
62
  uri = URI.parse(self.href)
58
63
  connection.put(uri.path, resource_singular_name.to_sym => @params)
59
64
  end
@@ -64,36 +69,47 @@ class Server
64
69
  def wait_for_state(st,timeout=1200)
65
70
  reload
66
71
  connection.logger("#{nickname} is #{self.state}")
72
+ step = 15
73
+ catch_early_terminated = 60 / step
67
74
  while(timeout > 0)
68
75
  return true if state =~ /#{st}/
69
76
  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')
70
- sleep 30
71
- timeout -= 30
77
+ 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/
72
78
  connection.logger("waiting for server #{nickname} to go #{st}, state is #{state}")
79
+ if state =~ /terminated|stopped|inactive|error/ and st !~ /terminated|stopped|inactive|error/
80
+ if catch_early_terminated <= 0
81
+ raise "FATAL error, this server terminated when waiting for #{st}: #{nickname}"
82
+ end
83
+ catch_early_terminated -= 1
84
+ end
85
+ sleep step
86
+ timeout -= step
73
87
  reload
74
88
  end
75
89
  raise "FATAL, this server #{self.audit_link} timed out waiting for the state to be #{st}" if timeout <= 0
76
90
  end
77
91
 
78
92
  # waits until the server is operational and dns_name is available
79
- def wait_for_operational_with_dns
93
+ def wait_for_operational_with_dns(state_wait_timeout=1200)
80
94
  timeout = 600
81
- wait_for_state("operational")
95
+ wait_for_state("operational", state_wait_timeout)
96
+ step = 15
82
97
  while(timeout > 0)
83
98
  self.settings
84
- break if self['dns-name'] && !self['dns-name'].empty? && self['private-dns-name'] && !self['private-dns-name'].empty?
85
- connection.logger "waiting for dns-name for #{self.nickname}"
86
- sleep 30
87
- timeout -= 30
99
+ break if self.reachable_ip
100
+ connection.logger "waiting for IP for #{self.nickname}"
101
+ sleep step
102
+ timeout -= step
88
103
  end
89
- connection.logger "got DNS: #{self['dns-name']}"
90
- raise "FATAL, this server #{self.audit_link} timed out waiting for DNS" if timeout <= 0
104
+ connection.logger "got IP: #{self.reachable_ip}"
105
+ raise "FATAL, this server #{self.audit_link} timed out waiting for IP" if timeout <= 0
91
106
  end
92
107
 
93
108
  def audit_link
94
109
  # proof of concept for now
95
- server_id = self.href.split(/\//).last
96
- audit_href = "https://my.rightscale.com/servers/#{server_id}#audit_entries"
110
+ # server_id = self.href.split(/\//).last
111
+ # audit_href = "https://my.rightscale.com/servers/#{server_id}#auditentries"
112
+ audit_href = self.href.gsub(/api\//,"") + "#auditentries"
97
113
  "<a href='#{audit_href}'>#{audit_href}</a>"
98
114
  end
99
115
 
@@ -116,6 +132,16 @@ class Server
116
132
  end
117
133
  end
118
134
 
135
+ def force_stop
136
+ if self.current_instance_href
137
+ t = URI.parse(self.href)
138
+ connection.post(t.path + '/stop')
139
+ connection.post(t.path + '/stop')
140
+ else
141
+ connection.logger("WARNING: was in #{self.state} and had a current_instance_href so skiping stop call")
142
+ end
143
+ end
144
+
119
145
  # Uses ServerInternal api to start and stop EBS based instances
120
146
  def start_ebs
121
147
  @server_internal = ServerInternal.new(:href => self.href)
@@ -129,7 +155,7 @@ class Server
129
155
 
130
156
  # Works on v4 and v5 images.
131
157
  # *executable can be an <~Executable> or <~RightScript>
132
- def run_executable(executable, opts=nil)
158
+ def run_executable(executable, opts=nil, ignore_lock=false)
133
159
  script_options = Hash.new
134
160
  script_options[:server] = Hash.new
135
161
  if executable.is_a?(Executable)
@@ -143,7 +169,7 @@ class Server
143
169
  else
144
170
  raise "Invalid class passed to run_executable, needs Executable or RightScript, was:#{executable.class}"
145
171
  end
146
-
172
+
147
173
  if not opts.nil? and opts.has_key?(:ignore_lock)
148
174
  script_options[:server][:ignore_lock] = "true"
149
175
  opts.delete(:ignore_lock)
@@ -151,6 +177,7 @@ class Server
151
177
 
152
178
  serv_href = URI.parse(self.href)
153
179
  script_options[:server][:parameters] = opts unless opts.nil?
180
+ script_options[:server][:ignore_lock] = "true" if ignore_lock
154
181
  location = connection.post(serv_href.path + '/run_executable', script_options)
155
182
  AuditEntry.new('href' => location)
156
183
  end
@@ -167,16 +194,32 @@ class Server
167
194
  script_options[:server][:parameters] = opts unless opts.nil?
168
195
  location = connection.post(serv_href.path + '/run_script', script_options)
169
196
  Status.new('href' => location)
170
- end
197
+ end
171
198
 
172
199
  def set_input(name, value)
173
200
  serv_href = URI.parse(self.href)
174
201
  connection.put(serv_href.path, :server => {:parameters => {name.to_sym => value} })
175
202
  end
176
203
 
177
- def set_inputs(hash)
204
+ def set_inputs(hash = {})
205
+ set_current_inputs(hash)
206
+ set_next_inputs(hash)
207
+ end
208
+
209
+ def set_current_inputs(hash = {})
210
+ if self.current_instance_href and self.state != "stopped"
211
+ self.reload_as_current
212
+ serv_href = URI.parse(self.href)
213
+ connection.put(serv_href.path, :server => {:parameters => hash})
214
+ settings
215
+ self.reload_as_next
216
+ end
217
+ end
218
+
219
+ def set_next_inputs(hash = {})
178
220
  serv_href = URI.parse(self.href)
179
221
  connection.put(serv_href.path, :server => {:parameters => hash})
222
+ settings
180
223
  end
181
224
 
182
225
  def set_template(href)
@@ -191,7 +234,7 @@ class Server
191
234
 
192
235
  def attach_volume(params)
193
236
  hash = {}
194
- hash[:server] = params
237
+ hash[:server] = params
195
238
  serv_href = URI.parse(self.href)
196
239
  connection.post(serv_href.path + "/attach_volume", hash)
197
240
  end
@@ -211,7 +254,7 @@ class Server
211
254
  reload
212
255
  old_state = self.state
213
256
  serv_href = URI.parse(self.href)
214
- connection.post(serv_href.path + "/reboot")
257
+ connection.post(serv_href.path + "/reboot")
215
258
  if wait_for_state
216
259
  wait_for_state_change(old_state)
217
260
  end
@@ -220,7 +263,7 @@ class Server
220
263
  def relaunch
221
264
  self.stop
222
265
  self.wait_for_state("stopped")
223
- self.start
266
+ self.start
224
267
  end
225
268
 
226
269
  def wait_for_state_change(old_state = nil)
@@ -238,23 +281,129 @@ class Server
238
281
  raise("FATAL: timeout after #{timeout}s waiting for state change")
239
282
  end
240
283
 
241
- # Save the servers parameters to the current server (instead of the next server)
242
- def save_current
243
- uri = URI.parse(self.href)
244
- connection.put(uri.path + "/current", resource_singular_name.to_sym => @params)
284
+ # Reload the server's basic information from the current server instance
285
+ def reload_as_current
286
+ uri = URI.parse(self.href + "/current")
287
+ @params.merge! connection.get(uri.path)
245
288
  end
246
289
 
247
- # Load server's settings from the current server (instead of the next server)
248
- def settings_current
249
- serv_href = URI.parse(self.href)
250
- @params.merge! connection.get(serv_href.path + "/current" + "/settings")
290
+ # Reload the server's basic information from the next server instance
291
+ def reload_as_next
292
+ uri = URI.parse(self.href.gsub(/\/current/,""))
293
+ @params.merge! connection.get(uri.path)
251
294
  end
252
295
 
253
- # Reload the server's basic information from the current server.
254
- def reload_current
255
- uri = URI.parse(self.href)
256
- @params ? @params.merge!(connection.get(uri.path + "/current")) : @params = connection.get(uri.path)
296
+ # Complex logic for determining the cloud_id of even a stopped server
297
+ def cloud_id
298
+ self.settings
299
+ if self.state == "operational"
300
+ return self["cloud_id"]
301
+ end
302
+ cloud_ids = RestConnection::AWS_CLOUDS.map { |hsh| hsh["cloud_id"] }
303
+
304
+ api0_1 = false
305
+ begin
306
+ api0_1 = true if Ec2SshKeyInternal.find_all
307
+ rescue
308
+ end
309
+
310
+ # Try ssh keys
311
+ if self.ec2_ssh_key_href and api0_1
312
+ ref = self.ec2_ssh_key_href
313
+ cloud_ids.each { |cloud|
314
+ if Ec2SshKeyInternal.find_by_cloud_id(cloud.to_s).select { |o| o.href == ref }.first
315
+ return cloud
316
+ end
317
+ }
318
+ end
319
+
320
+ # Try security groups
321
+ if self.ec2_security_groups_href
322
+ self.ec2_security_groups_href.each { |sg|
323
+ cloud_ids.each { |cloud|
324
+ if Ec2SecurityGroup.find_by_cloud_id(cloud.to_s).select { |o| o.href == sg }.first
325
+ return cloud
326
+ end
327
+ }
328
+ }
329
+ end
330
+
331
+ raise "Could not determine cloud_id...try setting an ssh key or security group"
257
332
  end
258
333
 
259
- end
334
+ def run_executable_and_wait_for_completed(executable, opts=nil)
335
+ run_executable(executable, opts).wait_for_completed
336
+ end
337
+
338
+ def dns_name
339
+ self.settings unless self["dns_name"]
340
+ self["dns_name"]
341
+ end
342
+
343
+ def private_ip
344
+ self.settings unless @params["private-ip-address"]
345
+ @params["private-ip-address"]
346
+ end
347
+
348
+ def reachable_ip
349
+ return self.dns_name if self.dns_name
350
+ return self.private_ip if self.private_ip
351
+ nil
352
+ end
353
+
354
+ # Override Taggable mixin so that it sets tags on both next and current instances
355
+ def tags(*args)
356
+ self.reload_as_next if self.href =~ /current/
357
+ super(*args)
358
+ end
359
+
360
+ def current_tags(reload=true)
361
+ self.reload_as_next if self.href =~ /current/
362
+ ret = []
363
+ if self.current_instance_href
364
+ ret = Tag.search_by_href(self.current_instance_href).map { |h| h["name"] }
365
+ end
366
+ ret
367
+ end
368
+
369
+ def add_tags(*args)
370
+ self.reload_as_next if self.href =~ /current/
371
+ return false if args.empty?
372
+ args.uniq!
373
+ Tag.set(self.href, args)
374
+ Tag.set(self.current_instance_href, args) if self.current_instance_href
375
+ self.tags(true)
376
+ end
260
377
 
378
+ def remove_tags(*args)
379
+ self.reload_as_next if self.href =~ /current/
380
+ return false if args.empty?
381
+ args.uniq!
382
+ Tag.unset(self.href, args)
383
+ Tag.unset(self.current_instance_href, args) if self.current_instance_href
384
+ self.tags(true)
385
+ end
386
+
387
+ def get_tags_by_namespace(namespace)
388
+ ret = {}
389
+ tags = {"self" => self.tags(true)}
390
+ tags["current_instance"] = self.current_tags if self.current_instance_href
391
+ tags.each { |res,ary|
392
+ ret[res] ||= {}
393
+ ary.each { |tag|
394
+ next unless tag.start_with?("#{namespace}:")
395
+ key = tag.split("=").first.split(":")[1..-1].join(":")
396
+ value = tag.split(":")[1..-1].join(":").split("=")[1..-1].join("=")
397
+ ret[res][key] = value
398
+ }
399
+ }
400
+ return ret
401
+ end
402
+
403
+ def clear_tags(namespace = nil)
404
+ tags = self.tags(true)
405
+ tags.deep_merge! self.current_tags if self.current_instance_href
406
+ tags = tags.select { |tag| tag.start_with?("#{namespace}:") } if namespace
407
+ self.remove_tags(*tags)
408
+ end
409
+ end