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.
- 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
|