Class: Backend

Inherits:
Object
  • Object
show all
Extended by:
MethodLoggerHelper, PrivateAttrAccessor, TrackAttributes
Defined in:
app/models/backend.rb

Overview

Provides access to the real backend which is loaded at runtime. All API calls will be automatically wrapped and delegated to the real backend.

Constant Summary collapse

API_VERSION =

Expose API_VERSION

'1.0.0'
BACKEND_TYPES =

Supported backend types

%w(compute storage network).freeze
DEFAULT_CACHE_EXPIRATION =

Default cache expiration

20.minutes

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from MethodLoggerHelper

extended

Methods included from PrivateAttrAccessor

private_attr_accessor, private_attr_reader, private_attr_writer

Methods included from TrackAttributes

attr_accessors, attr_readers, attr_writers, extended

Constructor Details

#initialize(delegated_user = nil, backend_names = {}, options = {}, server_properties = nil) ⇒ Backend

Instantiate backend model.

Parameters:

  • delegated_user (Hash) (defaults to: nil)

    user information

  • backend_names (Hash) (defaults to: {})

    names keyed by backend type

  • options (Hash) (defaults to: {})

    backend options keyed by backend type

  • server_properties (Hash) (defaults to: nil)

    global server options


31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'app/models/backend.rb', line 31

def initialize(delegated_user = nil, backend_names = {}, options = {}, server_properties = nil)
  @server_properties = server_properties || ROCCI_SERVER_CONFIG.common
  @options = {}
  @backend_instances = {}

  BACKEND_TYPES.each do |backend_type|
    backend_name = backend_names[backend_type] || ROCCI_SERVER_CONFIG.common.backend[backend_type]
    backend_class = self.class.load_backend_class(backend_name, backend_type)
    @options[backend_type] = options[backend_type] || ROCCI_SERVER_CONFIG.backends.send(backend_name.to_sym)

    Rails.logger.debug "[#{self.class}] Instantiating #{backend_class} " \
                       "for delegated_user=#{delegated_user.inspect} " \
                       "with options=#{self.options[backend_type]} and server_properties=#{self.server_properties}"

    @backend_instances[backend_type] = backend_class.new(
      delegated_user, self.options[backend_type],
      self.server_properties, Rails.logger,
      self.class.dalli_instance_factory(
        "#{backend_name}_#{backend_type}",
        self.server_properties.memcaches,
        { expire_after: DEFAULT_CACHE_EXPIRATION },
        "#{self.server_properties.hostname}_#{self.server_properties.port}"
      )
    )
  end

  inject_backends
end

Instance Attribute Details

#optionsObject (readonly)

Exposing a few attributes


22
23
24
# File 'app/models/backend.rb', line 22

def options
  @options
end

#server_propertiesObject (readonly)

Exposing a few attributes


22
23
24
# File 'app/models/backend.rb', line 22

def server_properties
  @server_properties
end

Class Method Details

.check_version(api_version, backend_version) ⇒ true, false

Checks backend version against the declared API version.

Examples:

Backend.check_version('1.0', '2.5')

Parameters:

  • api_version (String)

    current API version of the server

  • backend_version (String)

    API version of the backend

Returns:

  • (true, false)

    result of the check or raised exception


134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'app/models/backend.rb', line 134

def check_version(api_version, backend_version)
  s_major, s_minor, s_fix = api_version.split('.')
  b_major, b_minor, b_fix = backend_version.split('.')

  unless s_major == b_major
    message = "Backend reports API_VERSION=#{backend_version} and cannot be loaded because SERVER_API_VERSION=#{api_version}"
    Rails.logger.error "[#{self}] #{message}"
    fail Errors::BackendApiVersionMismatchError, message
  end

  unless s_minor == b_minor
    Rails.logger.warn "[#{self}] Backend reports API_VERSION=#{backend_version} and SERVER_API_VERSION=#{api_version}"
  end

  true
end

.dalli_instance_factory(backend_name, endpoints = nil, options = {}, instance_namespace = nil) ⇒ Dalli::Client

Constructs a backend-specific Dalli instance for caching purposes.

Examples:

Backend.dalli_instance_factory("dummy", "localhost:11211", { :expire_after => 20.minutes })
# => #<Dalli::Client>

Parameters:

  • backend_name (String)

    name of the target backend, for namespacing

  • endpoints (String) (defaults to: nil)

    memcache endpoints, address:port

  • options (Hash) (defaults to: {})

    options for Dalli::Client

  • instance_namespace (String) (defaults to: nil)

    string used to differentiate between different instance namespaces

Returns:

  • (Dalli::Client)

    constructed Dalli::Client instance


162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'app/models/backend.rb', line 162

def dalli_instance_factory(backend_name, endpoints = nil, options = {}, instance_namespace = nil)
  fail ArgumentError, 'Dalli instance cannot be constructed without a backend_name!' if backend_name.blank?
  endpoints ||= 'localhost:11211'

  defaults = { compress: true }
  defaults.merge! options

  if instance_namespace.blank?
    defaults[:namespace] = "ROCCIServer.backend_cache.#{backend_name}"
  else
    defaults[:namespace] = "ROCCIServer_#{instance_namespace}.backend_cache.#{backend_name}"
  end

  Dalli::Client.new(endpoints, defaults)
end

.load_backend_class(backend_name, backend_type) ⇒ Class

Matches the given backend name with the real backend class. Raises an exception if such a backend does not exist.

Examples:

Backend.load_backend_class('dummy', 'compute') #=> Backends::Dummy::Compute

Parameters:

  • backend_name (String)

    name of the chosen backend

  • backend_type (String)

    type of the chosen backend

Returns:

  • (Class)

    a class of the given backend


109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'app/models/backend.rb', line 109

def load_backend_class(backend_name, backend_type)
  backend_name = backend_name.camelize
  backend_type = backend_type.camelize
  Rails.logger.info "[#{self}] Loading Backends::#{backend_name}::#{backend_type}"

  begin
    backend_class = Backends.const_get(backend_name).const_get(backend_type)
  rescue NameError => err
    message = "There is no such valid backend available! " \
              "[Backends::#{backend_name}::#{backend_type}] #{err.message}"
    Rails.logger.error "[#{self}] #{message}"
    raise ArgumentError, message
  end

  backend_class
end

Instance Method Details

#get_extensionsOcci::Collection

Returns a collection of custom mixins introduced (and specific for) all enabled backends. Only mixins and actions are allowed.

Returns:

  • (Occi::Collection)

    all registered extensions in a collection


93
94
95
96
97
# File 'app/models/backend.rb', line 93

def get_extensions
  collection = Occi::Collection.new
  BACKEND_TYPES.each { |backend_type| collection.merge! backend_instances[backend_type].get_extensions }
  collection
end