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.
- data/README.rdoc +3 -3
- data/Rakefile +5 -4
- data/VERSION +1 -1
- data/git_hooks/post-commit.disabled +4 -0
- data/git_hooks/post-merge.disabled +4 -0
- data/git_hooks/pre-commit +53 -0
- data/lib/rest_connection.rb +75 -12
- data/lib/rest_connection/patches.rb +106 -0
- data/lib/rest_connection/rightscale/account.rb +27 -0
- data/lib/rest_connection/rightscale/alert_spec.rb +2 -2
- data/lib/rest_connection/rightscale/audit_entry.rb +5 -5
- data/lib/rest_connection/rightscale/child_account.rb +27 -0
- data/lib/rest_connection/rightscale/cloud.rb +26 -0
- data/lib/rest_connection/rightscale/credential.rb +1 -1
- data/lib/rest_connection/rightscale/deployment.rb +33 -7
- data/lib/rest_connection/rightscale/ec2_ebs_snapshot.rb +3 -1
- data/lib/rest_connection/rightscale/ec2_ebs_volume.rb +3 -1
- data/lib/rest_connection/rightscale/ec2_elastic_ip.rb +1 -1
- data/lib/rest_connection/rightscale/ec2_security_group.rb +2 -2
- data/lib/rest_connection/rightscale/ec2_server_array.rb +7 -5
- data/lib/rest_connection/rightscale/ec2_ssh_key.rb +11 -2
- data/lib/rest_connection/rightscale/ec2_ssh_key_internal.rb +2 -2
- data/lib/rest_connection/rightscale/executable.rb +4 -4
- data/lib/rest_connection/rightscale/instance.rb +5 -3
- data/lib/rest_connection/rightscale/instance_type.rb +31 -0
- data/lib/rest_connection/rightscale/macro.rb +19 -0
- data/lib/rest_connection/rightscale/mc_datacenter.rb +53 -0
- data/lib/rest_connection/rightscale/mc_deployment.rb +60 -0
- data/lib/rest_connection/rightscale/mc_image.rb +42 -0
- data/lib/rest_connection/rightscale/mc_instance.rb +170 -0
- data/lib/rest_connection/rightscale/mc_instance_type.rb +47 -0
- data/lib/rest_connection/rightscale/mc_multi_cloud_image.rb +58 -0
- data/lib/rest_connection/rightscale/mc_multi_cloud_image_setting.rb +46 -0
- data/lib/rest_connection/rightscale/mc_security_group.rb +42 -0
- data/lib/rest_connection/rightscale/mc_server.rb +250 -6
- data/lib/rest_connection/rightscale/mc_server_array.rb +44 -0
- data/lib/rest_connection/rightscale/mc_server_template.rb +53 -0
- data/lib/rest_connection/rightscale/mc_ssh_key.rb +50 -0
- data/lib/rest_connection/rightscale/mc_tag.rb +69 -0
- data/lib/rest_connection/rightscale/mc_volume.rb +53 -0
- data/lib/rest_connection/rightscale/mc_volume_attachment.rb +48 -0
- data/lib/rest_connection/rightscale/mc_volume_snapshot.rb +55 -0
- data/lib/rest_connection/rightscale/mc_volume_type.rb +47 -0
- data/lib/rest_connection/rightscale/monitoring_metric.rb +35 -0
- data/lib/rest_connection/rightscale/multi_cloud_image.rb +16 -2
- data/lib/rest_connection/rightscale/multi_cloud_image_cloud_setting_internal.rb +2 -2
- data/lib/rest_connection/rightscale/multi_cloud_image_internal.rb +25 -1
- data/lib/rest_connection/rightscale/permission.rb +27 -0
- data/lib/rest_connection/rightscale/right_script.rb +4 -4
- data/lib/rest_connection/rightscale/right_script_internal.rb +4 -4
- data/lib/rest_connection/rightscale/rightscale_api_base.rb +69 -20
- data/lib/rest_connection/rightscale/rightscale_api_gateway.rb +198 -13
- data/lib/rest_connection/rightscale/rightscale_api_internal.rb +3 -3
- data/lib/rest_connection/rightscale/rightscale_api_mc_input.rb +32 -0
- data/lib/rest_connection/rightscale/rightscale_api_mc_taggable.rb +57 -0
- data/lib/rest_connection/rightscale/rightscale_api_resources.rb +32 -2
- data/lib/rest_connection/rightscale/rightscale_api_taggable.rb +116 -0
- data/lib/rest_connection/rightscale/rs_internal.rb +6 -6
- data/lib/rest_connection/rightscale/s3_bucket.rb +36 -0
- data/lib/rest_connection/rightscale/server.rb +186 -37
- data/lib/rest_connection/rightscale/server_interface.rb +264 -0
- data/lib/rest_connection/rightscale/server_internal.rb +3 -3
- data/lib/rest_connection/rightscale/server_template.rb +15 -4
- data/lib/rest_connection/rightscale/server_template_internal.rb +4 -4
- data/lib/rest_connection/rightscale/status.rb +2 -2
- data/lib/rest_connection/rightscale/tag.rb +2 -2
- data/lib/rest_connection/rightscale/task.rb +47 -0
- data/lib/rest_connection/rightscale/user.rb +27 -0
- data/lib/rest_connection/ssh_hax.rb +42 -40
- data/rest_connection.gemspec +5 -5
- data/spec/mcserver_spec.rb +17 -0
- data/spec/multi.rb +16 -0
- 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
|
-
|
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
|
-
|
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
|
85
|
-
connection.logger "waiting for
|
86
|
-
sleep
|
87
|
-
timeout -=
|
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
|
90
|
-
raise "FATAL, this server #{self.audit_link} timed out waiting for
|
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}#
|
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
|
-
#
|
242
|
-
def
|
243
|
-
uri = URI.parse(self.href)
|
244
|
-
connection.
|
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
|
-
#
|
248
|
-
def
|
249
|
-
|
250
|
-
@params.merge! connection.get(
|
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
|
-
#
|
254
|
-
def
|
255
|
-
|
256
|
-
|
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
|
-
|
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
|