right_api_client 1.5.1

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.
@@ -0,0 +1,13 @@
1
+ .DS_Store
2
+ nbproject
3
+ *login.yml
4
+ .loadpath
5
+ .project
6
+ .rvmrc
7
+ .bundle
8
+ .idea
9
+ .idea/*
10
+ coverage/*
11
+ doc/*
12
+ *~
13
+ Gemfile.lock
@@ -0,0 +1,5 @@
1
+ = right_api_client - CHANGELOG.rdoc
2
+
3
+
4
+ == right_api_client - 1.5.1
5
+ Initial public release, supports all of the calls in RightScale API 1.5.
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2010-2011, RightScale.com
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,196 @@
1
+ = RightScale API Client
2
+
3
+ The right_api_client gem simplifies the use of RightScale's MultiCloud API. It provides
4
+ a simple object model of the API resources, and handles all of the fine details involved
5
+ in making HTTP calls and translating their responses.
6
+ It is assumed that users are already familiar with the RightScale API:
7
+ - API Documentation: http://support.rightscale.com/12-Guides/RightScale_API_1.5
8
+ - API Reference Docs: http://support.rightscale.com/api1.5
9
+
10
+ == Installation
11
+ Ruby 1.9 is required.
12
+ gem install right_api_client
13
+
14
+ == Versioning
15
+ The right_api_client gem is versioned using the usual X.Y.Z notation, where X.Y is the
16
+ RightScale API version, and Z is the client version. For example, if you want to use
17
+ RightScale API 1.5, you should use the latest version of the 1.5 gem. This will ensure
18
+ that you get the latest bug fixes for the client that is compatible with that API version.
19
+
20
+ == Usage Instructions
21
+ New users can start with the following few lines of code and navigate their way around the API by following
22
+ the available methods. You can find your account id by logging into the RightScale dashboard (https://my.rightscale.com),
23
+ navigate to the Settings > Account Settings page. The account is is at the end of the browser address bar.
24
+ require 'right_api_client'
25
+ @client = RightApi::Client.new(:email => 'my@email.com', :password => 'my_password', :account_id => 'my_account_id')
26
+ puts "Available methods: #{@client.api_methods}"
27
+
28
+ The client makes working with and getting to know the API much easier. It spiders the API dynamically to
29
+ discover its resources on the fly. At every step, the user has the ability to query api_methods(), which
30
+ indicates the potential methods that can be called. The <tt>config/login.yml.example</tt> file provides
31
+ details of different login parameters.
32
+
33
+ === Making API calls
34
+ Essentially, just follow the RightScale API documentation (available from http://support.rightscale.com)
35
+ and treat every resource in the paths as objects that can call other objects using the dot (.) operator:
36
+
37
+ Examples:
38
+ - Index: /api/clouds/:cloud_id/datacenters => @client.clouds(:id => :cloud_id).show.datacenters.index
39
+ - Show: /api/clouds/:cloud_id/datacenters/:id => @client.clouds(:id => :cloud_id).show.datacenters(:id => :datacenter_id).show
40
+ - Create: /api/deployments/:deployment_id/servers => @client.deployments(:id => :deployment_id).show.servers.create
41
+ - Update: /api/deployments/:deployment_id/servers/:id => @client.deployments(:id => :deployment_id).show.servers(:id => :server_id).update
42
+ - Destroy: /api/deployments/:deployment_id/servers/:id => @client.deployments(:id => :deployment_id).show.servers(:id => :server_id).destroy
43
+ - An action: /api/servers/:server_id/launch => @client.servers(:id => :server_id).show.launch
44
+
45
+ As seen above, whenever you need to chain methods, you must call .show before specifying the next method.
46
+
47
+ === Parameters
48
+ Pass-in parameters to the method that they belong to. Lets say you want to filter on the index for deployments:
49
+ @client.deployments.index(:filter => ['name==my_deployment'])
50
+ The filter is the parameter for the index call and not the deployment call.
51
+
52
+ === Logging HTTP Requests
53
+ The HTTP calls made by right_api_client can be logged in two ways:
54
+ 1. Log to a file
55
+ @client.log('~/right_api_client.log')
56
+ 2. Log to SDTOUT
57
+ @client.log(STDOUT)
58
+
59
+ == Examples
60
+ Get a list of all servers (aka doing an Index call)
61
+ @client.servers.index
62
+
63
+ Get a list of all servers in a deployment
64
+ @client.deployments(:id => 'my_deployment_id').show.servers.index
65
+
66
+ Get a particular server (aka doing a Show call)
67
+ @client.servers(:id => 'my_server_id').show
68
+
69
+ Creating a server involves setting up the required parameters, then calling the create method
70
+ server_template_href = @client.server_templates.index(:filter => ['name==Base ServerTemplate']).first.href
71
+ cloud = @client.clouds(:id => 'my_cloud_id').show
72
+ params = { :server => {
73
+ :name => 'Test Server',
74
+ :deployment_href => @client.deployments(:id => 'my_deployment_id').show.href,
75
+ :instance => {
76
+ :server_template_href => server_template_href,
77
+ :cloud_href => cloud.href,
78
+ :security_group_hrefs => [cloud.security_groups.index(:filter => ['name==default']).first.href],
79
+ :ssh_key_href => cloud.ssh_keys.index.first.href,
80
+ :datacenter_href => cloud.datacenters.index.first.href
81
+ }}}
82
+ new_server = @client.servers.create(params)
83
+ new_server.api_methods
84
+
85
+ Launch the newly created server. Inputs are a bit tricky so they have to be set in a long string
86
+ inputs = "inputs[][name]=NAME1&inputs[][value]=text:VALUE1&inputs[][name]=NAME2&inputs[][value]=text:VALUE2"
87
+ new_server.show.launch(inputs)
88
+
89
+ Run a script on the server. The API does not currently expose right_scripts, hence, the script href has
90
+ to be retrieved from the dashboard and put in the following href format.
91
+ script_href = "right_script_href=/api/right_scripts/382371"
92
+ task = new_server.show.current_instance.show.run_executable(script_href + "&inputs[][name]=TEST_NAME&inputs[][value]=text:VALUE1")
93
+ task.show.api_methods
94
+
95
+ Update the server's name
96
+ params = { :server => {:name => 'New Server Name'}}
97
+ @client.servers(:id => 'my_server_id').update(params)
98
+
99
+ Terminate the server (i.e. shutdown its current_instance)
100
+ @client.servers(:id => 'my_server_id').show.terminate
101
+
102
+ Destroy the server (i.e. delete it)
103
+ @client.servers(:id => 'my_server_id').destroy
104
+
105
+ == Object Types
106
+ The client returns 3 types of objects:
107
+ - <b>Resources</b>: returned when you are querying a collection of resources, e.g.: <tt>client.deployments</tt>
108
+ - <b>Resource</b>: returned when you specify an id and therefore a specific resource, e.g.: <tt>@client.deployments(:id => :deployment_id)</tt>
109
+ - When the content-type is type=collection then an array of Resource objects will be returned, e.g.: <tt>@client.deployments.index</tt>
110
+ - When the content-type is not a collection then a Resource object will be returned, e.g.: <tt>@client.deployments(:id => deployment_id).show</tt>
111
+ - <b>ResourceDetail</b>: returned when you do a .show on a Resource, e.g.: <tt>client.deployments(:id => deployment_id).show</tt>
112
+ <b>On all 3 types of objects you can query <tt>.api_methods</tt> to see a list of available methods, e.g.: <tt>client.deployments.api_methods</tt></b>
113
+
114
+ === Exceptions:
115
+ - <tt>inputs.index</tt> will return an array of ResourceDetail objects since you cannot do a .show on an input
116
+ - <tt>session.index</tt> will return a ResourceDetail object since you cannot do a .show on a session
117
+ - <tt>tags.by_resource, tags.by_tag</tt> will return an array of ResourceDetail objects since you cannot do a .show on a resource_tag
118
+ - <tt>monitoring_metrics(:id => :m_m_id).show.data</tt> will return a ResourceDetail object since you cannot do
119
+ a .show on a monitoring_metric_data
120
+
121
+ == Instance Facing Calls:
122
+ The client also supports 'instance facing calls', which use the instance_token to login.
123
+ Unlike regular email-password logins, instance-facing-calls are limited in the amount of allowable calls.
124
+ Since in most of the cases, the calls are scoped to the instance's cloud (or the instance itself), the cloud_id and
125
+ the instance_id will be automatically recorded by the client, so that the user does not need to specify it.
126
+
127
+ === Examples
128
+ @instance_client = RightApi::Client.new(:instance_token => 'my_token', :account_id => 'my_account_id')
129
+ @instance_client.volume_attachments links to /api/clouds/:cloud_id/volume_attachments
130
+ @instance_client.volumes_snapshots links to /api/clouds/:cloud_id/volumes_snapshots
131
+ @instance_client.volumes_types links to /api/clouds/:cloud_id/volumes_types
132
+ @instance_client.volumes links to /api/clouds/:cloud_id/volumes
133
+ @instance_client.backups links to /api/backups
134
+ @instance_client.live_tasks(:id) links to /api/clouds/:cloud_id/instances/:instance_id/live/tasks/:id
135
+
136
+ === Notes
137
+ For volume_attachments and volumes_snapshots you can also go through the volume:
138
+ @instance_client.volumes(:id => :volume_id).show.volume_attachments
139
+ which maps to:
140
+ /api/clouds/:cloud_id/volumes/:volume_id/volume_attachment
141
+ The instance's volume_attachments can be accessed using:
142
+ @instance_client.get_instance.volume_attachments
143
+ which maps to:
144
+ /api/clouds/:cloud_id/instances/:instance_id/volume_attachments
145
+
146
+ Because the cloud_id and the instance_id are automatically added by the client, scripts that work for regular
147
+ email-password logins will have to be modified for instance-facing calls. The main reason behind this is the
148
+ inability for instance-facing calls to access the clouds resource (i.e.: <tt>@instance_client.clouds(:id=> :cloud_id).show</tt> will fail)
149
+
150
+ When you query <tt>api_methods</tt>, it will list all of the methods that one sees with regular email-password logins.
151
+ Due to the limiting scope of the instance-facing calls, only a subset of these methods can be called
152
+ (see the API Reference Docs for valid methods). If you call a method that instance's are not authorized to access,
153
+ you will get a 403 Permission Denied error.
154
+
155
+
156
+ = Design Decisions
157
+ In the code, we only hard-code CRUD operations for resources. We use the .show and .index methods to make the client
158
+ more efficient. Since it dynamically creates methods it needs to query the API at times. The .show and the .index make
159
+ it explicit that querying needs to take place. Without them a GET would have to be queried every step of the way
160
+ (i.e.: the index call would be client.deployments, and the create call would be client.deployments.create which would
161
+ first do an index call).
162
+
163
+ <b>In general, when a new API resource is added, you need to indicate in the client whether index, show, create, update
164
+ and delete methods are allowed for that resource.</b>
165
+
166
+ == Special Cases
167
+ === Returning resource_types that are not actual API resources:
168
+ - tags:
169
+ - by_resource, by_tag: both return a COLLECTION of resource_type = RESOURCE_TAG
170
+ - no show or index is defined for that resource_type, therefore return a collection of ResourceDetail objects
171
+ - data:
172
+ - querying .data for monitoring_metrics:
173
+ - no show is defined for that resource_type, therefore return a ResourceDetail object
174
+
175
+ === Index call does not act like an index call
176
+ - session:
177
+ - session.index should act like a show call and not like an index call (since you cannot query show).
178
+ Therefore it should return a ResourceDetail object
179
+ - inputs
180
+ - inputs.index cannot return a collection of Resource objects since .show is not allowed. Therefore it should
181
+ return a collection of ResourceDetail objects
182
+
183
+ === Having a resource_type that cannot be accurately determined from the URL:
184
+ - In server_arrays.show: resource_type = current_instance(s) (although it should be instance(s))
185
+ - In multi_cloud_images.show: resource_type = setting(s) (although it should be multi_cloud_image_setting)
186
+ Put these special cases in the <tt>RightApi::Helper::INCONSISTENT_RESOURCE_TYPES</tt> hash.
187
+
188
+ === Method defined on the generic resource_type itself
189
+ - 'instances' => {:multi_terminate => 'do_post', :multi_run_executable => 'do_post'},
190
+ - 'inputs' => {:multi_update => 'do_put'},
191
+ - 'tags' => {:by_tag => 'do_post', :by_resource => 'do_post', :multi_add => 'do_post', :multi_delete =>'do_post'},
192
+ - 'backups' => {:cleanup => 'do_post'}
193
+ Put these special cases in the <tt>RightApi::Helper::RESOURCE_TYPE_SPECIAL_ACTIONS</tt> hash.
194
+
195
+ === Resources are not linked together
196
+ - In ResourceDetail, resource_type = Instance, need live_tasks as a method.
@@ -0,0 +1,17 @@
1
+ require File.expand_path('../lib/right_api_client', __FILE__)
2
+ require 'rake'
3
+ require 'spec/rake/spectask'
4
+
5
+ task :build do
6
+ system "gem build right_api_client.gemspec"
7
+ end
8
+
9
+ task :release => :build do
10
+ system "gem push right_api_client-#{RightApi::Client::VERSION}.gem"
11
+ end
12
+
13
+ Spec::Rake::SpecTask.new('spec') do |t|
14
+ t.spec_files = Dir.glob('spec/*_spec.rb')
15
+ t.spec_opts << '--format nested'
16
+ t.spec_opts << '--colour'
17
+ end
@@ -0,0 +1,25 @@
1
+ # The API login details can be put in a YAML file. This is an example login.yml which
2
+ # will be needed for login_to_client_irb.rb quick login method.
3
+
4
+ # Account ID is a required parameter. You can find your account number by logging into the
5
+ # RightScale dashboard (https://my.rightscale.com), navigate to the Settings > Account Settings page.
6
+ # The account number is at the end of the browser address bar.
7
+ :account_id: my_account_id
8
+
9
+ # There are three login mechanisms:
10
+
11
+ # 1. Use the following parameters to login with your email and password:
12
+ :email: my@email.com
13
+ :password: my_password
14
+
15
+ # 2. Use the following parameter to login with an instance_token:
16
+ # To find the instance_token, launch a server, navigate to the info page and under
17
+ # User Data box, you will see RS_API_TOKEN = your_account_id:the_instance_token
18
+ :instance_token: my_instance_token
19
+
20
+ # 3. Use the following parameter to login with pre-authenticated cookies:
21
+ :cookies: my_cookie_string
22
+
23
+ # The following are optional parameters can also be set:
24
+ :api_url: (this defaults to https://my.rightscale.com)
25
+ :api_version: (this defaults to 1.5)
@@ -0,0 +1 @@
1
+ require File.expand_path('../right_api_client/client', __FILE__)
@@ -0,0 +1,245 @@
1
+ require 'rest_client'
2
+ require 'json'
3
+ require 'set'
4
+ require 'cgi'
5
+
6
+ require File.expand_path('../version', __FILE__) unless defined?(RightApi::Client::VERSION)
7
+ require File.expand_path('../helper', __FILE__)
8
+ require File.expand_path('../resource', __FILE__)
9
+ require File.expand_path('../resource_detail', __FILE__)
10
+ require File.expand_path('../resources', __FILE__)
11
+
12
+ # RightApiClient has the generic get/post/delete/put calls that are used by resources
13
+ module RightApi
14
+ class Client
15
+ include Helper
16
+
17
+ ROOT_RESOURCE = '/api/session'
18
+ ROOT_INSTANCE_RESOURCE = '/api/session/instance'
19
+ DEFAULT_API_URL = 'https://my.rightscale.com'
20
+
21
+ # permitted parameters for initializing
22
+ AUTH_PARAMS = %w(email password account_id api_url api_version cookies instance_token)
23
+ attr_reader :cookies, :instance_token
24
+
25
+ def initialize(args)
26
+ @api_url, @api_version = DEFAULT_API_URL, API_VERSION
27
+ # Initializing all instance variables from hash
28
+ args.each { |key,value|
29
+ instance_variable_set("@#{key}", value) if value && AUTH_PARAMS.include?(key.to_s)
30
+ } if args.is_a? Hash
31
+
32
+ raise 'This API client is only compatible with the RightScale API 1.5 and upwards.' if (Float(@api_version) < 1.5)
33
+ @rest_client = RestClient::Resource.new(@api_url)
34
+
35
+ # There are three options for login: credentials, instance token, or if the user already
36
+ # has the cookies they can just use those. See config/login.yml.example for more info.
37
+ @cookies ||= login()
38
+
39
+ # Add the top level links for instance_facing_calls
40
+ if @instance_token
41
+ resource_type, path, data = self.do_get(ROOT_INSTANCE_RESOURCE)
42
+ instance_href = get_href_from_links(data['links'])
43
+ cloud_href = instance_href.split('/instances')[0]
44
+
45
+ define_instance_method(:get_instance) do |*params|
46
+ type, instance_path, instance_data = self.do_get(ROOT_INSTANCE_RESOURCE)
47
+ RightApi::ResourceDetail.new(self, type, instance_path, instance_data)
48
+ end
49
+
50
+ Helper::INSTANCE_FACING_RESOURCES.each do |meth|
51
+ define_instance_method(meth) do |*args|
52
+ obj_path = cloud_href + '/' + meth.to_s
53
+ # Following are special cases that need to over-ride the obj_path
54
+ obj_path = '/api/backups' if meth == :backups
55
+ obj_path = instance_href + '/live/tasks' if meth == :live_tasks
56
+ if has_id(*args)
57
+ obj_path = add_id_and_params_to_path(obj_path, *args)
58
+ RightApi::Resource.process(self, get_singular(meth), obj_path)
59
+ else
60
+ RightApi::Resources.new(self, obj_path, meth.to_s)
61
+ end
62
+ end
63
+ end
64
+ else
65
+ # Session is the root resource that has links to all the base resources
66
+ define_instance_method(:session) do |*params|
67
+ RightApi::Resources.new(self, ROOT_RESOURCE, 'session')
68
+ end
69
+ # Allow the base resources to be accessed directly
70
+ get_associated_resources(self, session.index.links, nil)
71
+ end
72
+ end
73
+
74
+ def to_s
75
+ "#<RightApi::Client>"
76
+ end
77
+
78
+ # Log HTTP calls to file (file can be STDOUT as well)
79
+ def log(file)
80
+ RestClient.log = file
81
+ end
82
+
83
+ # Users shouldn't need to call the following methods directly
84
+
85
+ def login
86
+ if @instance_token
87
+ params = {
88
+ 'instance_token' => @instance_token
89
+ }
90
+ path = ROOT_INSTANCE_RESOURCE
91
+ else
92
+ params = {
93
+ 'email' => @email,
94
+ 'password' => @password,
95
+ }
96
+ path = ROOT_RESOURCE
97
+ end
98
+ params['account_href'] = "/api/accounts/#{@account_id}"
99
+
100
+ response = @rest_client[path].post(params, 'X_API_VERSION' => @api_version) do |response, request, result|
101
+ case response.code
102
+ when 302
103
+ response
104
+ else
105
+ response.return!(request, result)
106
+ end
107
+ end
108
+ response.cookies
109
+ end
110
+
111
+ # Returns the request headers
112
+ def headers
113
+ {'X_API_VERSION' => @api_version, :cookies => @cookies, :accept => :json}
114
+ end
115
+
116
+ # Generic get
117
+ # params are NOT read only
118
+ def do_get(path, params={})
119
+ # Resource id is a special param as it needs to be added to the path
120
+ path = add_id_and_params_to_path(path, params)
121
+
122
+ begin
123
+ # Return content type so the resulting resource object knows what kind of resource it is.
124
+ resource_type, body = @rest_client[path].get(headers) do |response, request, result|
125
+ case response.code
126
+ when 200
127
+ # Get the resource_type from the content_type, the resource_type will
128
+ # be used later to add relevant methods to relevant resources.
129
+ type = ''
130
+ if result.content_type.index('rightscale')
131
+ type = get_resource_type(result.content_type)
132
+ end
133
+
134
+ [type, response.body]
135
+ else
136
+ raise "Unexpected response #{response.code.to_s}, #{response.body}"
137
+ end
138
+ end
139
+ #Session cookie is expired or invalid
140
+ rescue RuntimeError => e
141
+ if re_login?(e)
142
+ @cookies = login()
143
+ retry
144
+ else
145
+ raise e
146
+ end
147
+ end
148
+
149
+ data = JSON.parse(body)
150
+ [resource_type, path, data]
151
+ end
152
+
153
+ # Generic post
154
+ def do_post(path, params={})
155
+ begin
156
+ @rest_client[path].post(params, headers) do |response, request, result|
157
+ case response.code
158
+ when 201, 202
159
+ # Create and return the resource
160
+ href = response.headers[:location]
161
+ relative_href = href.split(@api_url)[-1]
162
+ # Return the resource that was just created
163
+ # Determine the resource_type from the href (eg. api/clouds/id).
164
+ # This is based on the assumption that we can determine the resource_type without doing a do_get
165
+ resource_type = get_singular(relative_href.split('/')[-2])
166
+ RightApi::Resource.process(self, resource_type, relative_href)
167
+ when 200..299
168
+ # This is needed for the tags Resource -- which returns a 200 and has a content type
169
+ # therefore, ResourceDetail objects needs to be returned
170
+ if response.code == 200 && response.headers[:content_type].index('rightscale')
171
+ resource_type = get_resource_type(response.headers[:content_type])
172
+ data = JSON.parse(response)
173
+ # Resource_tag is returned after querying tags.by_resource or tags.by_tags.
174
+ # You cannot do a show on a resource_tag, but that is basically what we want to do
175
+ data.map { |obj|
176
+ RightApi::ResourceDetail.new(self, resource_type, path, obj)
177
+ }
178
+ else
179
+ response.return!(request, result)
180
+ end
181
+ else
182
+ raise "Unexpected response #{response.code.to_s}, #{response.body}"
183
+ end
184
+ end
185
+ rescue RuntimeError => e
186
+ if re_login?(e)
187
+ @cookies = login()
188
+ retry
189
+ else
190
+ raise e
191
+ end
192
+ end
193
+ end
194
+
195
+ # Generic delete
196
+ def do_delete(path)
197
+ begin
198
+ @rest_client[path].delete(headers) do |response|
199
+ case response.code
200
+ when 200, 204
201
+ else
202
+ raise "Unexpected response #{response.code.to_s}, #{response.body}"
203
+ end
204
+ end
205
+ rescue RuntimeError => e
206
+ if re_login?(e)
207
+ @cookies = login()
208
+ retry
209
+ else
210
+ raise e
211
+ end
212
+ end
213
+ end
214
+
215
+ # Generic put
216
+ def do_put(path, params={})
217
+ begin
218
+ @rest_client[path].put(params, headers) do |response|
219
+ case response.code
220
+ when 204
221
+ else
222
+ raise "Unexpected response #{response.code.to_s}, #{response.body}"
223
+ end
224
+ end
225
+ rescue RuntimeError => e
226
+ if re_login?(e)
227
+ @cookies = login()
228
+ retry
229
+ else
230
+ raise e
231
+ end
232
+ end
233
+ end
234
+
235
+ def re_login?(e)
236
+ e.message.index('403') && e.message =~ %r(.*Session cookie is expired or invalid)
237
+ end
238
+
239
+ # returns the resource_type
240
+ def get_resource_type(content_type)
241
+ content_type.scan(/\.rightscale\.(.*)\+json/)[0][0]
242
+ end
243
+ end
244
+ end
245
+