rest_connection 0.0.23 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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