Class: SoftLayer::Service

Inherits:
Object
  • Object
show all
Defined in:
lib/softlayer/Service.rb

Overview

SoftLayer API Service

Instances of this class are the runtime representation of services in the SoftLayer API. They handle communication with the SoftLayer servers.

You typically should not need to create services directly. instead, you should be creating a client and then using it to obtain individual services. For example:

client = SoftLayer::Client.new(:username => "Joe", :api_key=>"feeddeadbeefbadfood...")
 = client.service_named("Account") # returns the SoftLayer_Account service
 = client[:Account] # Exactly the same as above

For backward compatibility, a service can be constructed by passing client initialization options, however if you do so you will need to prepend the “SoftLayer_” on the front of the service name. For Example:

account_service = SoftLayer::Service("SoftLayer_Account",
:username=>"<your user name here>"
:api_key=>"<your api key here>")

A service communicates with the SoftLayer API through the the XML-RPC interface using Ruby’s built in classes

Once you have a service, you can invoke methods in the service like this:

account_service.getOpenTickets
=> {... lots of information here representing the list of open tickets ...}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(service_name, options = {}) ⇒ Service

Returns a new instance of Service.

Raises:

  • (ArgumentError)


79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/softlayer/Service.rb', line 79

def initialize (service_name, options = {})
  raise ArgumentError,"Please provide a service name" if service_name.nil? || service_name.empty?

  # remember the service name
  @service_name = service_name;

  # Collect the keys relevant to client creation and pass them on to construct
  # the client if one is needed.
  client_keys = [:username, :api_key, :endpoint_url]
  client_options = options.inject({}) do |new_hash, pair|
    if client_keys.include? pair[0]
      new_hash[pair[0]] = pair[1]
    end

    new_hash
  end

  # if the options hash already has a client
  # go ahead and use it
  if options.has_key? :client
    if !client_options.empty?
      raise RuntimeError, "Attempting to construct a service both with a client, and with client initialization options. Only one or the other should be provided."
    end

    @client = options[:client]
  else
    # Accepting client initialization options here
    # is a backward-compatibility feature.

    if $DEBUG
      $stderr.puts %q{
Creating services with Client initialization options is deprecated and may be removed
in a future release. Please change your code to create a client and obtain a service
using either client.service_named('<service_name_here>') or client['<service_name_here>']}
    end

    @client = SoftLayer::Client.new(client_options)
  end

  # this has proven to be very helpful during debugging. It helps prevent infinite recursion
  # when you don't get a method call just right
  @method_missing_call_depth = 0 if $DEBUG
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *args, &block) ⇒ Object

This is the primary mechanism by which requests are made. If you call the service with a method it doesn’t understand, it will send a call to the endpoint for a method of the same name.



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/softlayer/Service.rb', line 180

def method_missing(method_name, *args, &block)
  # During development, if you end up with a stray name in some
  # code, you can end up in an infinite recursive loop as method_missing
  # tries to resolve that name (believe me... it happens).
  # This mechanism looks for what it considers to be an unreasonable call
  # depth and kills the loop quickly.
  if($DEBUG)
    @method_missing_call_depth += 1
    if @method_missing_call_depth > 3 # 3 is somewhat arbitrary... really the call depth should only ever be 1
      @method_missing_call_depth = 0
      raise "stop infinite recursion #{method_name}, #{args.inspect}"
    end
  end

  # if we're in debug mode, we put out a little helpful information
  puts "SoftLayer::Service#method_missing called #{method_name}, #{args.inspect}" if $DEBUG

  if(!block && method_name.to_s.match(/[[:alnum:]]+/))
    result = call_softlayer_api_with_params(method_name, nil, args);
  else
    result = super
  end

  if($DEBUG)
    @method_missing_call_depth -= 1
  end

  return result
end

Instance Attribute Details

#clientObject (readonly)

Returns the value of attribute client.



77
78
79
# File 'lib/softlayer/Service.rb', line 77

def client
  @client
end

#service_nameObject (readonly)

The name of the service that this object calls. Cannot be empty or nil.



76
77
78
# File 'lib/softlayer/Service.rb', line 76

def service_name
  @service_name
end

Instance Method Details

#call_softlayer_api_with_params(method_name, parameters, args) ⇒ Object

Issue an HTTP request to call the given method from the SoftLayer API with the parameters and arguments given.

Parameters are information about the call, the object mask or the particular object in the SoftLayer API you are calling.

Arguments are the arguments to the SoftLayer method that you wish to invoke.

This is intended to be used in the internal processing of method_missing and need not be called directly.



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/softlayer/Service.rb', line 221

def call_softlayer_api_with_params(method_name, parameters, args)
  additional_headers = {};

  # The client knows about authentication, so ask him for the auth headers
  authentication_headers = self.client.authentication_headers
  additional_headers.merge!(authentication_headers)

  if parameters && parameters.server_object_filter
    additional_headers.merge!("#{@service_name}ObjectFilter" => parameters.server_object_filter)
  end

  # Object masks go into the headers too.
  if parameters && parameters.server_object_mask
    object_mask = parameters.server_object_mask
    additional_headers.merge!("SoftLayer_ObjectMask" => { "mask" => object_mask }) unless object_mask.empty?
  end

  # Result limits go into the headers
  if (parameters && parameters.server_result_limit)
    additional_headers.merge!("resultLimit" => { "limit" => parameters.server_result_limit, "offset" => (parameters.server_result_offset || 0) })
  end

  # Add an object id to the headers.
  if parameters && parameters.server_object_id
    additional_headers.merge!("#{@service_name}InitParameters" => { "id" => parameters.server_object_id })
  end

  # This is a workaround for a potential problem that arises from mis-using the
  # API. If you call SoftLayer_Virtual_Guest and you call the getObject method
  # but pass a virtual guest as a parameter, what happens is the getObject method
  # is called through an HTTP POST verb and the API creates a new VirtualServer that
  # is a copy of the one you passed in.
  #
  # The counter-intuitive creation of a new Virtual Server is unexpected and, even worse,
  # is something you can be billed for. To prevent that, we ignore the request
  # body on a "getObject" call and print out a warning.
  if (method_name == :getObject) && (nil != args) && (!args.empty?) then
    $stderr.puts "Warning - The getObject method takes no parameters. The parameters you have provided will be ignored."
    args = nil
  end

  # Collect all the different header pieces into a single hash that
  # will become the first argument to the call.
  call_headers = {
    "headers" => additional_headers
  }

  begin
    call_value = xmlrpc_client.call(method_name.to_s, call_headers, *args)
  rescue XMLRPC::FaultException => e
    puts "A XMLRPC Fault was returned #{e}" if $DEBUG
    raise
  end

  return call_value
end

#object_filter(filter) ⇒ Object

Add an object filter to the request.



172
173
174
175
# File 'lib/softlayer/Service.rb', line 172

def object_filter(filter)
  proxy = APIParameterFilter.new(self)
  return proxy.object_filter(filter)
end

#object_mask(*args) ⇒ Object

Use this as part of a method call chain to add an object mask to the request. The arguments to object mask should be well formed Extended Object Mask strings:

ticket_service.object_mask("mask[ticket.createDate, ticket.modifyDate]", "mask(SoftLayer_Some_Type).aProperty").getObject

The object_mask becomes part of the request sent to the server



156
157
158
159
# File 'lib/softlayer/Service.rb', line 156

def object_mask(*args)
  proxy = APIParameterFilter.new(self)
  return proxy.object_mask(*args)
end

#object_with_id(object_of_interest) ⇒ Object

Use this as part of a method call chain to identify a particular object as the target of the request. The parameter is the SoftLayer object identifier you are interested in. For example, this call would return the ticket whose ID is 35212

ticket_service.object_with_id(35212).getObject


143
144
145
146
# File 'lib/softlayer/Service.rb', line 143

def object_with_id(object_of_interest)
  proxy = APIParameterFilter.new(self)
  return proxy.object_with_id(object_of_interest)
end

Returns a related service with the given service name. The related service will use the same client as this service



125
126
127
# File 'lib/softlayer/Service.rb', line 125

def related_service_named(service_name)
  @client.service_named(service_name)
end

#result_limit(offset, limit) ⇒ Object

Use this as part of a method call chain to reduce the number of results returned from the server. For example, if the server has a list of 100 entities and you only want 5 of them, you can get the first five by using result_limit(0,5). Then for the next 5 you would use result_limit(5,5), then result_limit(10,5) etc.



166
167
168
169
# File 'lib/softlayer/Service.rb', line 166

def result_limit(offset, limit)
  proxy = APIParameterFilter.new(self)
  return proxy.result_limit(offset, limit)
end

#targetObject

Added here so that the interface of this class matches that of APIParameterFilter. In APIParameterFilter the target is a service. In a service, the target is itself.



132
133
134
# File 'lib/softlayer/Service.rb', line 132

def target
  return self
end

#to_aryObject

If this is not defined for Service, then when you print a service object the code will try to convert it to an array and end up calling method_missing

We define this here to prevent odd calls to the SoftLayer API



282
283
284
# File 'lib/softlayer/Service.rb', line 282

def to_ary
  nil
end