You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@deltacloud.apache.org by jp...@redhat.com on 2013/05/02 13:06:37 UTC
POC: DC lib
Hello Deltacloud community,
I started on implementing Deltacloud library (discussed few weeks ago on this
mailing list). Because I have not enough time to continue on this, I'm sending
an experimental patch with work I did so far. If anyone would appreciate DC as a
lib and has some time, feel free to continue on this or check it for inspiration.
This patch wraps DC drivers in a library (Deltacloud::Library) which defines a set
of methods for working with providers. The set of methods is taken from Michal's
ruby-deltacloud-client. This lib is called from the existing DC sinatra app -
by calling 'driver', this lib is actually called now (naming is confusing), also
this lib can be used directly from your app or script, e.g.:
#!/usr/bin/ruby
require 'deltacloud/api'
mock = Deltacloud.new('mock', :user => 'mockuser', :password => 'mockpassword')
inst = mock.create_instance(mock.images.first, {
:name => "libtest-#{Time.now.to_i}"
})
mock.instances.each do |i|
puts "#{i.id} - #{i.name}"
end
mock.stop_instance(inst.id)
mock.destroy_instance(inst.id)
This patch is far from being finished, though basic listing of objects (instances,
images, hw profiles...) and instance launching should work both in DC service
and when using lib directly. Some methods (driver helper) were moved around in
a hacky way to make things work both from lib and from current DC service.
Jan
[PATCH] POC: deltacloud as a lib
Posted by jp...@redhat.com.
From: Jan Provaznik <jp...@redhat.com>
Kick-off of the dc lib implementation. This patch shouldn't be pushed,
it's only experimental POC.
---
server/lib/deltacloud/api.rb | 97 ++++++++++++++++++----
server/lib/deltacloud/api/methods.rb | 12 +++
server/lib/deltacloud/api/methods/address.rb | 58 +++++++++++++
server/lib/deltacloud/api/methods/blob.rb | 91 ++++++++++++++++++++
server/lib/deltacloud/api/methods/bucket.rb | 54 ++++++++++++
server/lib/deltacloud/api/methods/driver.rb | 39 +++++++++
server/lib/deltacloud/api/methods/firewall.rb | 66 +++++++++++++++
.../lib/deltacloud/api/methods/hardware_profile.rb | 25 ++++++
server/lib/deltacloud/api/methods/image.rb | 45 ++++++++++
server/lib/deltacloud/api/methods/instance.rb | 73 ++++++++++++++++
server/lib/deltacloud/api/methods/key.rb | 57 +++++++++++++
server/lib/deltacloud/api/methods/realm.rb | 26 ++++++
.../lib/deltacloud/api/methods/storage_snapshot.rb | 60 +++++++++++++
.../lib/deltacloud/api/methods/storage_volume.rb | 68 +++++++++++++++
server/lib/deltacloud/collections/addresses.rb | 12 +--
server/lib/deltacloud/collections/base.rb | 4 +
server/lib/deltacloud/collections/buckets.rb | 34 ++++----
server/lib/deltacloud/collections/drivers.rb | 2 +-
server/lib/deltacloud/collections/firewalls.rb | 12 +--
.../deltacloud/collections/hardware_profiles.rb | 2 +-
server/lib/deltacloud/collections/images.rb | 6 +-
server/lib/deltacloud/collections/instances.rb | 20 ++---
server/lib/deltacloud/collections/keys.rb | 6 +-
.../lib/deltacloud/collections/load_balancers.rb | 22 ++---
.../deltacloud/collections/storage_snapshots.rb | 6 +-
.../lib/deltacloud/collections/storage_volumes.rb | 14 ++--
server/lib/deltacloud/drivers/base_driver.rb | 17 ----
server/lib/deltacloud/drivers/mock/mock_driver.rb | 3 +-
server/lib/deltacloud/helpers/deltacloud_helper.rb | 16 ++--
server/lib/deltacloud/helpers/driver_helper.rb | 30 +++----
server/lib/deltacloud/models/instance.rb | 4 +-
server/lib/deltacloud/server.rb | 21 ++++-
server/views/api/show.xml.haml | 2 +-
server/views/buckets/new.html.haml | 2 +-
server/views/instances/new.html.haml | 18 ++--
server/views/instances/show.xml.haml | 2 +-
36 files changed, 881 insertions(+), 145 deletions(-)
create mode 100644 server/lib/deltacloud/api/methods.rb
create mode 100644 server/lib/deltacloud/api/methods/address.rb
create mode 100644 server/lib/deltacloud/api/methods/blob.rb
create mode 100644 server/lib/deltacloud/api/methods/bucket.rb
create mode 100644 server/lib/deltacloud/api/methods/driver.rb
create mode 100644 server/lib/deltacloud/api/methods/firewall.rb
create mode 100644 server/lib/deltacloud/api/methods/hardware_profile.rb
create mode 100644 server/lib/deltacloud/api/methods/image.rb
create mode 100644 server/lib/deltacloud/api/methods/instance.rb
create mode 100644 server/lib/deltacloud/api/methods/key.rb
create mode 100644 server/lib/deltacloud/api/methods/realm.rb
create mode 100644 server/lib/deltacloud/api/methods/storage_snapshot.rb
create mode 100644 server/lib/deltacloud/api/methods/storage_volume.rb
diff --git a/server/lib/deltacloud/api.rb b/server/lib/deltacloud/api.rb
index 295bbd2..15d72ce 100644
--- a/server/lib/deltacloud/api.rb
+++ b/server/lib/deltacloud/api.rb
@@ -76,16 +76,28 @@ module Deltacloud
class Library
include Helpers::Drivers
- attr_reader :backend, :credentials
+ require_relative 'api/methods'
+ include Deltacloud::Api::Methods::Address
+ include Deltacloud::Api::Methods::Blob
+ include Deltacloud::Api::Methods::Bucket
+ include Deltacloud::Api::Methods::Driver
+ include Deltacloud::Api::Methods::Firewall
+ include Deltacloud::Api::Methods::HardwareProfile
+ include Deltacloud::Api::Methods::Image
+ include Deltacloud::Api::Methods::Instance
+ include Deltacloud::Api::Methods::Key
+ include Deltacloud::Api::Methods::Realm
+ include Deltacloud::Api::Methods::StorageSnapshot
+ include Deltacloud::Api::Methods::StorageVolume
+
+ attr_accessor :credentials
+ attr_reader :driver_name, :provider
def initialize(driver_name, opts={}, &block)
- Thread.current[:driver] = driver_name.to_s
- Thread.current[:provider] = opts[:provider]
- @backend = driver
- opts[:user] ||= 'mockuser'
- opts[:password] ||= 'mockpassword'
+ @driver_name = driver_name.to_s
+ @provider = opts[:provider]
@credentials = OpenStruct.new(:user => opts[:user], :password => opts[:password])
- yield backend if block_given?
+ #yield if block_given?
end
def version
@@ -93,11 +105,13 @@ module Deltacloud
end
def current_provider
- Thread.current[:provider]
+ #Thread.current[:provider]
+ provider
end
def current_driver
- Thread.current[:driver]
+ #Thread.current[:driver]
+ driver_name
end
def providers
@@ -112,17 +126,66 @@ module Deltacloud
providers.find { |p| p.id == opts[:id] }
end
- def method_missing(name, *args)
- return super unless backend.respond_to? name
- begin
- params = ([@credentials] + args).flatten
- backend.send(name, *params)
- rescue ArgumentError => e
- puts "[W] Wrong arguments for #{name}. Omitting credentials injection. (#{e.message})"
- backend.send(name, *args)
+ def has_feature?(c, f)
+ backend.has_feature?(c, f)
+ end
+
+ def instance_state_machine
+ backend.instance_state_machine
+ end
+
+ def configured_providers
+ backend.configured_providers
+ end
+
+ def features(&block)
+ backend.class.features(&block)
+ end
+
+ #def method_missing(name, *args)
+ # return super unless backend.respond_to? name
+ # begin
+ # params = ([@credentials] + args).flatten
+ # backend.send(name, *params)
+ # rescue ArgumentError => e
+ # puts "[W] Wrong arguments for #{name}. Omitting credentials injection. (#{e.message})"
+ # backend.send(name, *args)
+ # end
+ #end
+
+ private
+
+ def backend
+ unless @backend
+ $:.unshift File.dirname(__FILE__)
+ begin
+ @backend = driver_class
+ rescue NameError => e
+ if require(driver_source_name)
+ retry
+ else
+ raise(e.message)
+ end
+ rescue LoadError => e
+ raise "[ERROR] The driver '#{driver_name}' is unknown or not installed (#{driver_source_name})\n" +
+ "\n#{e.message}\n"
+ end
end
+ @backend
+ end
+
+ def driver_class_name
+ driver_name.camelize
+ end
+
+ def driver_source_name
+ File.join('drivers', driver_name, driver_name + '_driver.rb')
end
+ def driver_class
+ m = Deltacloud::Drivers.const_get(driver_class_name)
+ m.const_get(driver_class_name + "Driver").new
+ end
end
def self.new(driver_name, opts={}, &block)
diff --git a/server/lib/deltacloud/api/methods.rb b/server/lib/deltacloud/api/methods.rb
new file mode 100644
index 0000000..86cf5dc
--- /dev/null
+++ b/server/lib/deltacloud/api/methods.rb
@@ -0,0 +1,12 @@
+require_relative './methods/driver'
+require_relative './methods/realm'
+require_relative './methods/hardware_profile'
+require_relative './methods/image'
+require_relative './methods/instance'
+require_relative './methods/storage_volume'
+require_relative './methods/storage_snapshot'
+require_relative './methods/key'
+require_relative './methods/address'
+require_relative './methods/bucket'
+require_relative './methods/blob'
+require_relative './methods/firewall'
diff --git a/server/lib/deltacloud/api/methods/address.rb b/server/lib/deltacloud/api/methods/address.rb
new file mode 100644
index 0000000..0d26103
--- /dev/null
+++ b/server/lib/deltacloud/api/methods/address.rb
@@ -0,0 +1,58 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership. The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+module Deltacloud::Api
+ module Methods
+ module Address
+
+ # Retrieve list of all address entities
+ #
+ # Filter options:
+ #
+ # - :id -> Filter entities using 'id' attribute
+ #
+ def addresses(filter_opts={})
+ backend.addresses(credentials, filter_opts)
+ end
+
+ # Retrieve the single address entity
+ #
+ # - address_id -> Address entity to retrieve
+ #
+ def address(address_id)
+ backend.addresses(credentials, {:id => address_id}).first
+ end
+
+ # Create a new address
+ #
+ def create_address
+ backend.create_address(credentials, {})
+ end
+
+ def destroy_address(address_id)
+ backend.destroy_address(credentials, { :id => address_id})
+ end
+
+ def associate_address(address_id, instance_id)
+ backend.associate_address(credentials, { :id => address_id, :instance_id => instance_id})
+ end
+
+ def disassociate_address(address_id)
+ backend.disassociate_address(credentials, { :id => address_id })
+ end
+
+ end
+ end
+end
diff --git a/server/lib/deltacloud/api/methods/blob.rb b/server/lib/deltacloud/api/methods/blob.rb
new file mode 100644
index 0000000..3c9c165
--- /dev/null
+++ b/server/lib/deltacloud/api/methods/blob.rb
@@ -0,0 +1,91 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership. The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+module Deltacloud::Api
+ module Methods
+ module Blob
+
+ ## Retrieve list of all blob entities from given bucket
+ ##
+ #def blobs(bucket_id=nil)
+ # raise error.new("The :bucket_id cannot be nil.") if bucket_id.nil?
+ # bucket(bucket_id).blob_ids.map { |blob_id| blob(bucket_id, blob_id) }
+ #end
+
+ ## Retrieve the single blob entity
+ ##
+ ## - blob_id -> Blob entity to retrieve
+ ##
+ #def blob(bucket_id, blob_id)
+ # model(:blob).convert(
+ # self,
+ # connection.get(api_uri("buckets/#{bucket_id}/#{blob_id}"))
+ # )
+ #end
+
+ ## Create a new blob
+ ##
+ ## - bucket_id -> A bucket ID that new blob should belong to
+ ## - blob_name -> A name for new blob
+ ## - blob_data -> Data stored in this blob
+ ## - create_opts
+ ## - :user_metadata -> A Ruby +Hash+ with key => value metadata
+ ##
+ #def create_blob(bucket_id, blob_name, blob_data, create_opts={})
+ # must_support! :buckets
+ # create_opts.merge!(convert_meta_params(create_opts.delete(:user_metadata)))
+ # response = connection.post(api_uri("buckets/#{bucket_id}")) do |request|
+ # request.params = create_opts.merge(
+ # :blob_id => blob_name,
+ # :blob_data => blob_data
+ # )
+ # end
+ # model(:blob).convert(self, response.body)
+ #end
+
+ ## Destroy given bucket blob
+ ##
+ #def destroy_blob(bucket_id, blob_id)
+ # must_support! :buckets
+ # r = connection.delete(api_uri("buckets/#{bucket_id}/#{blob_id}"))
+ # r.status == 204
+ #end
+
+ #private
+
+ ## Convert the user_metadata into POST params compatible with
+ ## blob creation
+ ##
+ ## - params -> Simple Ruby +Hash+
+ ##
+ ## @return { :meta_params => COUNTER, :meta_name1 => '', :meta_value1 => ''}
+ ##
+ #def convert_meta_params(params)
+ # meta_params = {}
+ # counter = 0
+ # (params || {}).each do |key, value|
+ # counter += 1
+ # meta_params["meta_name#{counter}"] = key
+ # meta_params["meta_value#{counter}"] = value
+ # end
+ # if counter >= 1
+ # meta_params.merge!(:meta_params => counter.to_s)
+ # end
+ # meta_params
+ #end
+
+ end
+ end
+end
diff --git a/server/lib/deltacloud/api/methods/bucket.rb b/server/lib/deltacloud/api/methods/bucket.rb
new file mode 100644
index 0000000..1164768
--- /dev/null
+++ b/server/lib/deltacloud/api/methods/bucket.rb
@@ -0,0 +1,54 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership. The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+module Deltacloud::Api
+ module Methods
+ module Bucket
+
+ # Retrieve list of all bucket entities
+ #
+ # Filter options:
+ #
+ # - :id -> Filter entities using 'id' attribute
+ #
+ def buckets(filter_opts={})
+ backend.buckets(filter_opts)
+ end
+
+ # Retrieve the single bucket entity
+ #
+ # - bucket_id -> Bucket entity to retrieve
+ #
+ def bucket(bucket_id)
+ backend.buckets(credentials, {:id => bucket_id}).first
+ end
+
+ # Create a new bucket
+ #
+ # - create_opts
+ #
+ def create_bucket(name)
+ backend.create_bucket(credentials, {:name => name})
+ end
+
+ # Destroy given bucket
+ #
+ def destroy_bucket(bucket_id)
+ backend.delete_bucket(credentials, bucket_id)
+ end
+
+ end
+ end
+end
diff --git a/server/lib/deltacloud/api/methods/driver.rb b/server/lib/deltacloud/api/methods/driver.rb
new file mode 100644
index 0000000..a044164
--- /dev/null
+++ b/server/lib/deltacloud/api/methods/driver.rb
@@ -0,0 +1,39 @@
+module Deltacloud::Api
+ module Methods
+ module Driver
+
+ ## Retrieve list of all drivers
+ ##
+ ## Filter options:
+ ##
+ ## - :id -> Filter drivers using their 'id'
+ ## - :state -> Filter drivers by their 'state'
+ ##
+ #def drivers(filter_opts={})
+ # from_collection(
+ # :drivers,
+ # connection.get(api_uri('drivers'), filter_opts)
+ # )
+ #end
+
+ ## Retrieve the given driver
+ ##
+ ## - driver_id -> Driver to retrieve
+ ##
+ #def driver(driver_id)
+ # from_resource(
+ # :driver,
+ # connection.get(api_uri("drivers/#{driver_id}"))
+ # )
+ #end
+
+ ## List of the current driver providers
+ ##
+ #def providers
+ # driver(current_driver).providers
+ #end
+
+ end
+
+ end
+end
diff --git a/server/lib/deltacloud/api/methods/firewall.rb b/server/lib/deltacloud/api/methods/firewall.rb
new file mode 100644
index 0000000..887d422
--- /dev/null
+++ b/server/lib/deltacloud/api/methods/firewall.rb
@@ -0,0 +1,66 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership. The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+module Deltacloud::Api
+ module Methods
+ module Firewall
+
+ ## Retrieve list of all firewall entities
+ ##
+ ## Filter options:
+ ##
+ ## - :id -> Filter entities using 'id' attribute
+ ##
+ #def firewalls(filter_opts={})
+ # from_collection :firewalls,
+ # connection.get(api_uri('firewalls'), filter_opts)
+ #end
+
+ ## Retrieve the single firewall entity
+ ##
+ ## - firewall_id -> Firewall entity to retrieve
+ ##
+ #def firewall(firewall_id)
+ # from_resource :firewall,
+ # connection.get(api_uri("firewalls/#{firewall_id}"))
+ #end
+
+ ## Create a new firewall
+ ##
+ ## - create_opts
+ ##
+ #def create_firewall(name, create_opts={})
+ # create_resource :firewall, { :name => name }.merge(create_opts)
+ #end
+
+ #def destroy_firewall(firewall_id)
+ # destroy_resource :firewall, firewall_id
+ #end
+
+ #def add_firewall_rule(firewall_id, protocol, port_from, port_to, opts={})
+ # r = connection.post(api_uri("firewalls/#{firewall_id}/rules")) do |request|
+ # request.params = {
+ # :protocol => protocol,
+ # :port_from => port_from,
+ # :port_to => port_to
+ # }
+ # # TODO: Add support for sources
+ # end
+ # model(:firewall).convert(self, r.body)
+ #end
+
+ end
+ end
+end
diff --git a/server/lib/deltacloud/api/methods/hardware_profile.rb b/server/lib/deltacloud/api/methods/hardware_profile.rb
new file mode 100644
index 0000000..1f0435e
--- /dev/null
+++ b/server/lib/deltacloud/api/methods/hardware_profile.rb
@@ -0,0 +1,25 @@
+module Deltacloud::Api
+ module Methods
+ module HardwareProfile
+
+ # Retrieve list of all hardware_profiles
+ #
+ # Filter options:
+ #
+ # - :id -> Filter hardware_profiles using their 'id'
+ #
+ def hardware_profiles(filter_opts={})
+ backend.hardware_profiles(credentials, filter_opts)
+ end
+
+ # Retrieve the given hardware_profile
+ #
+ # - hardware_profile_id -> hardware_profile to retrieve
+ #
+ def hardware_profile(hwp_id)
+ backend.hardware_profile(credentials, hwp_id)
+ end
+
+ end
+ end
+end
diff --git a/server/lib/deltacloud/api/methods/image.rb b/server/lib/deltacloud/api/methods/image.rb
new file mode 100644
index 0000000..ba8c04a
--- /dev/null
+++ b/server/lib/deltacloud/api/methods/image.rb
@@ -0,0 +1,45 @@
+module Deltacloud::Api
+ module Methods
+ module Image
+
+ # Retrieve list of all images
+ #
+ # Filter options:
+ #
+ # - :id -> Filter images using their 'id'
+ # - :state -> Filter images by their 'state'
+ # - :architecture -> Filter images by their 'architecture'
+ #
+ def images(filter_opts={})
+ backend.images(credentials, filter_opts)
+ end
+
+ # Retrieve the given image
+ #
+ # - image_id -> Image to retrieve
+ #
+ def image(image_id)
+ backend.images(credentials, {:id => image_id}).first
+ end
+
+ # Create a new image from instance
+ #
+ # - instance_id -> The stopped instance used for creation
+ # - create_opts
+ # - :name -> Name of the new image
+ # - :description -> Description of the new image
+ #
+ def create_image(instance_id, create_opts={})
+ backend.create_image(credentials, { :instance_id => instance_id }.merge(create_opts))
+ end
+
+ # Destroy given image
+ # NOTE: This operation might not be supported for all drivers.
+ #
+ def destroy_image(image_id)
+ backend.destroy_image(credentials, image_id)
+ end
+
+ end
+ end
+end
diff --git a/server/lib/deltacloud/api/methods/instance.rb b/server/lib/deltacloud/api/methods/instance.rb
new file mode 100644
index 0000000..bb27443
--- /dev/null
+++ b/server/lib/deltacloud/api/methods/instance.rb
@@ -0,0 +1,73 @@
+module Deltacloud::Api
+ module Methods
+ module Instance
+
+ # Retrieve list of all instances
+ #
+ # Filter options:
+ #
+ # - :id -> Filter instances using their 'id'
+ # - :state -> Filter instances by their 'state'
+ # - :realm_id -> Filter instances based on their 'realm_id'
+ #
+ def instances(filter_opts={})
+ backend.instances(credentials, filter_opts)
+ end
+
+ # Retrieve the given instance
+ #
+ # - instance_id -> Instance to retrieve
+ #
+ def instance(instance_id)
+ backend.instance(credentials, {:id => instance_id})
+ end
+
+ # Create a new instance
+ #
+ # - image_id -> Image to use for instance creation (img1, ami-12345, etc...)
+ # - create_opts -> Various options that DC support for the current
+ # provider.
+ #
+ # Returns created instance, or list of created instances or all instances.
+ #
+ def create_instance(image_id, create_opts={})
+ backend.create_instance(credentials, image_id, create_opts)
+ end
+
+ # Destroy the current +Instance+
+ # Returns 'true' if the response was 204 No Content
+ #
+ # - instance_id -> The 'id' of the Instance to destroy
+ #
+ def destroy_instance(instance_id)
+ # TODO: instance actions are more complicated, check current collections
+ # code
+ backend.destroy_instance(credentials, instance_id)
+ end
+
+ # Attempt to change the +Instance+ state to STOPPED
+ #
+ # - instance_id -> The 'id' of the Instance to stop
+ #
+ def stop_instance(instance_id)
+ backend.stop_instance(credentials, instance_id)
+ end
+
+ # Attempt to change the +Instance+ state to STARTED
+ #
+ # - instance_id -> The 'id' of the Instance to start
+ #
+ def start_instance(instance_id)
+ backend.start_instance(credentials, instance_id)
+ end
+
+ # Attempt to reboot the +Instance+
+ #
+ # - instance_id -> The 'id' of the Instance to reboot
+ #
+ def reboot_instance(instance_id)
+ backend.reboot_instance(credentials, instance_id)
+ end
+ end
+ end
+end
diff --git a/server/lib/deltacloud/api/methods/key.rb b/server/lib/deltacloud/api/methods/key.rb
new file mode 100644
index 0000000..fff0528
--- /dev/null
+++ b/server/lib/deltacloud/api/methods/key.rb
@@ -0,0 +1,57 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership. The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+module Deltacloud::Api
+ module Methods
+ module Key
+
+ # Retrieve list of all key entities
+ #
+ # Filter options:
+ #
+ # - :id -> Filter entities using 'id' attribute
+ #
+ def keys(filter_opts={})
+ backend.keys(credentials, filter_opts)
+ end
+
+ # Retrieve the single key entity
+ #
+ # - key_id -> Key entity to retrieve
+ #
+ def key(key_id)
+ backend.key(credentials, key_id)
+ end
+
+ # Create a new credentials to use with authentication
+ # to an +Instance+
+ #
+ # - key_name -> The name of the key
+ # - create_opts
+ # : public_key -> Your SSH public key (eg. ~/.ssh/id_rsa.pub)
+ #
+ def create_key(key_name, create_opts={})
+ backend.create_key(credentials, { :key_name => key_name }.merge(create_opts))
+ end
+
+ # Destroy the SSH key
+ #
+ def destroy_key(key_id)
+ backend.destroy_key(credentials, { :id => key_id })
+ end
+
+ end
+ end
+end
diff --git a/server/lib/deltacloud/api/methods/realm.rb b/server/lib/deltacloud/api/methods/realm.rb
new file mode 100644
index 0000000..28a322b
--- /dev/null
+++ b/server/lib/deltacloud/api/methods/realm.rb
@@ -0,0 +1,26 @@
+module Deltacloud::Api
+ module Methods
+ module Realm
+
+ # Retrieve list of all realms
+ #
+ # Filter options:
+ #
+ # - :id -> Filter realms using their 'id'
+ # - :state -> Filter realms by their 'state'
+ #
+ def realms(filter_opts={})
+ backend.realms(credentials, filter_opts)
+ end
+
+ # Retrieve the given realm
+ #
+ # - realm_id -> Instance to retrieve
+ #
+ def realm(realm_id)
+ backend.realm(credentials, realm_id)
+ end
+
+ end
+ end
+end
diff --git a/server/lib/deltacloud/api/methods/storage_snapshot.rb b/server/lib/deltacloud/api/methods/storage_snapshot.rb
new file mode 100644
index 0000000..411441c
--- /dev/null
+++ b/server/lib/deltacloud/api/methods/storage_snapshot.rb
@@ -0,0 +1,60 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership. The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+module Deltacloud::Api
+ module Methods
+ module StorageSnapshot
+
+ # Retrieve list of all storage_snapshot entities
+ #
+ # Filter options:
+ #
+ # - :id -> Filter entities using 'id' attribute
+ #
+ def storage_snapshots(filter_opts={})
+ backend.storage_snapshots(credentials, filter_opts)
+ end
+
+ # Retrieve the single storage_snapshot entity
+ #
+ # - storage_snapshot_id -> StorageSnapshot entity to retrieve
+ #
+ def storage_snapshot(storage_snapshot_id)
+ backend.storage_snapshots(credentials, {:id => snapshot_id}).first
+ end
+
+ # Create a new StorageSnapshot based on +volume_id+
+ #
+ # - volume_id -> ID of the +StorageVolume+ to create snapshot from
+ # - create_opts ->
+ # :name -> Name of the StorageSnapshot
+ # :description -> Description of the StorageSnapshot
+ #
+ def create_storage_snapshot(volume_id, create_opts={})
+ backend.create_storage_snapshot(credentials, create_opts.merge(:volume_id => volume_id))
+ end
+
+ # Destroy the current +StorageSnapshot+
+ # Returns 'true' if the response was 204 No Content
+ #
+ # - snapshot_id -> The 'id' of the snapshot to destroy
+ #
+ def destroy_storage_snapshot(snapshot_id)
+ backend.destroy_storage_snapshot(credentials, {:id => snapshot_id})
+ end
+
+ end
+ end
+end
diff --git a/server/lib/deltacloud/api/methods/storage_volume.rb b/server/lib/deltacloud/api/methods/storage_volume.rb
new file mode 100644
index 0000000..96dc2e5
--- /dev/null
+++ b/server/lib/deltacloud/api/methods/storage_volume.rb
@@ -0,0 +1,68 @@
+module Deltacloud::Api
+ module Methods
+ module StorageVolume
+
+ # Retrieve list of all storage_volumes
+ #
+ # Filter options:
+ #
+ # - :id -> Filter storage_volumes using their 'id'
+ # - :state -> Filter storage_volumes by their 'state'
+ #
+ def storage_volumes(filter_opts={})
+ backend.storage_volumes(credentials, filter_opts)
+ end
+
+ # Retrieve the given storage_volume
+ #
+ # - storage_volume_id -> Instance to retrieve
+ #
+ def storage_volume(storage_volume_id)
+ backend.storage_volumes(credentials, {:id => snapshot_id}).first
+ end
+
+ # Create new storage volume
+ #
+ # - :snapshot_id -> Snapshot to use for creating a new volume
+ # - :capacity -> Initial Volume capacity
+ # - :realm_id -> Create volume in this realm
+ # - :name -> Volume name
+ # - :description -> Volume description
+ #
+ # NOTE: Some create options might not be supported by backend cloud
+ #
+ def create_storage_volume(create_opts={})
+ backend.create_storage_volume(credentials, create_opts)
+ end
+
+ # Destroy the current +StorageVolume+
+ # Returns 'true' if the response was 204 No Content
+ #
+ # - volume_id -> The 'id' of the volume to destroy
+ #
+ def destroy_storage_volume(volume_id)
+ backend.destroy_storage_volume(credentials, {:id => volume_id})
+ end
+
+ # Attach the Storage Volume to the Instance
+ # The +device+ parameter could be used if supported.
+ #
+ # - volume_id -> Volume ID (eg. 'vol1')
+ # - instance_id -> Target Instance ID (eg. 'inst1')
+ # - device -> Target device in Instance (eg. '/dev/sda2')
+ #
+ def attach_storage_volume(volume_id, instance_id, device=nil)
+ backend.attach_storage_volume({:id => volume_id, :instance_id => instance_id, :device_id => device})
+ end
+
+ # Detach the Storage Volume from the Instance
+ #
+ # -volume_id -> Volume to detach
+ #
+ def detach_storage_volume(volume_id)
+ backend.detach_storage_volume({:id => volume_id, :instance_id => instance_id, :device_id => device})
+ end
+
+ end
+ end
+end
diff --git a/server/lib/deltacloud/collections/addresses.rb b/server/lib/deltacloud/collections/addresses.rb
index fb877d8..5abc467 100644
--- a/server/lib/deltacloud/collections/addresses.rb
+++ b/server/lib/deltacloud/collections/addresses.rb
@@ -19,8 +19,8 @@ module Deltacloud::Collections
set :capability, lambda { |m| driver.respond_to? m }
get '/addresses/:id/associate' do
- @address = driver.address(credentials, params )
- @instances = driver.instances(credentials)
+ @address = driver.address(params )
+ @instances = driver.instances
respond_to do |format|
format.html {haml :"addresses/associate"}
end
@@ -35,7 +35,7 @@ module Deltacloud::Collections
operation :create, :with_capability => :create_address do
description "Acquire a new IP address for use with your account."
control do
- @address = driver.create_address(credentials, {})
+ @address = driver.create_address({})
status 201 # Created
response['Location'] = address_url(@address.id)
respond_to do |format|
@@ -48,7 +48,7 @@ module Deltacloud::Collections
operation :destroy, :with_capability => :destroy_address do
control do
- driver.destroy_address(credentials, { :id => params[:id]})
+ driver.destroy_address({ :id => params[:id]})
status 204
respond_to do |format|
format.xml
@@ -63,7 +63,7 @@ module Deltacloud::Collections
param :id, :string, :required
param :instance_id, :string, :required
control do
- driver.associate_address(credentials, { :id => params[:id], :instance_id => params[:instance_id]})
+ driver.associate_address({ :id => params[:id], :instance_id => params[:instance_id]})
status 202 # Accepted
respond_to do |format|
format.xml
@@ -77,7 +77,7 @@ module Deltacloud::Collections
description "Disassociate an IP address from an instance"
param :id, :string, :required
control do
- driver.disassociate_address(credentials, { :id => params[:id] })
+ driver.disassociate_address({ :id => params[:id] })
status 202 # Accepted
respond_to do |format|
format.xml
diff --git a/server/lib/deltacloud/collections/base.rb b/server/lib/deltacloud/collections/base.rb
index e6d7333..b23e69c 100644
--- a/server/lib/deltacloud/collections/base.rb
+++ b/server/lib/deltacloud/collections/base.rb
@@ -25,6 +25,7 @@ module Deltacloud::Collections
helpers Deltacloud::Helpers::Drivers
helpers Sinatra::AuthHelper
+
helpers Deltacloud::Helpers::Application
helpers Sinatra::Rabbit::URLHelper
register Rack::RespondTo
@@ -66,6 +67,9 @@ module Deltacloud::Collections
before do
# Respond with 400, If we don't get a http Host header,
halt 400, "Unable to find HTTP Host header" if @env['HTTP_HOST'] == nil
+ # FIXME: temporary hack, consumed by driver_helper
+ Thread.current[:user] = credentials.user
+ Thread.current[:password] = credentials.password
end
after do
diff --git a/server/lib/deltacloud/collections/buckets.rb b/server/lib/deltacloud/collections/buckets.rb
index 18d1d15..6a22bba 100644
--- a/server/lib/deltacloud/collections/buckets.rb
+++ b/server/lib/deltacloud/collections/buckets.rb
@@ -20,7 +20,7 @@ module Deltacloud::Collections
set :capability, lambda { |m| driver.respond_to? m }
- check_features :for => lambda { |c, f| driver.class.has_feature?(c, f) }
+ check_features :for => lambda { |c, f| driver.has_feature?(c, f) }
new_route_for :buckets
@@ -34,7 +34,7 @@ module Deltacloud::Collections
head '/buckets/:bucket/:blob' do
@blob_id = params[:blob]
- @blob_metadata = driver.blob_metadata(credentials, {:id => params[:blob], 'bucket' => params[:bucket]})
+ @blob_metadata = driver.blob_metadata({:id => params[:blob], 'bucket' => params[:bucket]})
if @blob_metadata
@blob_metadata.each do |k,v|
headers["X-Deltacloud-Blobmeta-#{k}"] = v
@@ -52,7 +52,7 @@ module Deltacloud::Collections
put "/segmented_blob_operation/:bucket/:blob" do
case BlobHelper.segmented_blob_op_type(request)
when "init" then
- segmented_blob_id = driver.init_segmented_blob(credentials, {:id => params[:blob],
+ segmented_blob_id = driver.init_segmented_blob({:id => params[:blob],
:bucket => params[:bucket]})
headers["X-Deltacloud-SegmentedBlob"] = segmented_blob_id
status 204
@@ -67,7 +67,7 @@ module Deltacloud::Collections
when "finalize" then
segmented_blob_manifest = BlobHelper.extract_segmented_blob_manifest(request)
segmented_blob_id = BlobHelper.segmented_blob_id(request)
- @blob = driver.create_blob(credentials, params[:bucket], params[:blob], nil, {:segment_manifest=>segmented_blob_manifest, :segmented_blob_id=>segmented_blob_id})
+ @blob = driver.create_blob(params[:bucket], params[:blob], nil, {:segment_manifest=>segmented_blob_manifest, :segmented_blob_id=>segmented_blob_id})
respond_to do |format|
format.xml { haml :"blobs/show", :locals => { :blob => @blob } }
format.html { haml :"blobs/show", :locals => { :blob => @blob } }
@@ -84,7 +84,7 @@ module Deltacloud::Collections
elsif(env["BLOB_SUCCESS"]) #ie got a 200ok after putting blob
content_type = env["CONTENT_TYPE"]
content_type ||= ""
- @blob = driver.blob(credentials, {:id => params[:blob],
+ @blob = driver.blob({:id => params[:blob],
'bucket' => params[:bucket]})
respond_to do |format|
format.xml { haml :"blobs/show", :locals => { :blob => @blob } }
@@ -103,7 +103,7 @@ module Deltacloud::Collections
content_type = env['CONTENT_TYPE'] || ""
blob_data = {:tempfile => temp_file, :type => content_type}
user_meta = BlobHelper::extract_blob_metadata_hash(request.env)
- @blob = driver.create_blob(credentials, bucket_id, blob_id, blob_data, user_meta)
+ @blob = driver.create_blob(bucket_id, blob_id, blob_data, user_meta)
temp_file.delete
respond_to do |format|
format.xml { haml :"blobs/show", :locals => { :blob => @blob } }
@@ -122,7 +122,7 @@ module Deltacloud::Collections
operation :create, :with_capability => :create_bucket do
param :name, :string, :required
control do
- @bucket = driver.create_bucket(credentials, params[:name], params)
+ @bucket = driver.create_bucket(params[:name], params)
status 201
response['Location'] = bucket_url(@bucket.id)
respond_to do |format|
@@ -138,7 +138,7 @@ module Deltacloud::Collections
operation :destroy, :with_capability => :delete_bucket do
control do
- driver.delete_bucket(credentials, params[:id], params)
+ driver.delete_bucket(params[:id], params)
status 204
respond_to do |format|
format.xml
@@ -153,7 +153,7 @@ module Deltacloud::Collections
operation :show, :with_capability => :blob do
param :blob_id, :string, :required
control do
- @blob = driver.blob(credentials, { :id => params[:blob_id], 'bucket' => params[:id]} )
+ @blob = driver.blob({ :id => params[:blob_id], 'bucket' => params[:id]} )
if @blob
respond_to do |format|
format.xml { haml :"blobs/show", :locals => { :blob => @blob } }
@@ -187,7 +187,7 @@ module Deltacloud::Collections
user_meta[key] = value
end
end
- @blob = driver.create_blob(credentials, bucket_id, blob_id, blob_data, user_meta)
+ @blob = driver.create_blob(bucket_id, blob_id, blob_data, user_meta)
status 201
respond_to do |format|
format.xml { haml :"blobs/show", :locals => { :blob => @blob } }
@@ -202,7 +202,7 @@ module Deltacloud::Collections
control do
bucket_id = params[:id]
blob_id = params[:blob_id]
- driver.delete_blob(credentials, bucket_id, blob_id)
+ driver.delete_blob(bucket_id, blob_id)
status 204
respond_to do |format|
format.xml
@@ -220,7 +220,7 @@ module Deltacloud::Collections
if(env["BLOB_SUCCESS"]) #ie got a 200ok after putting blob
content_type = env["CONTENT_TYPE"]
content_type ||= ""
- @blob = driver.blob(credentials, {:id => params[:blob],
+ @blob = driver.blob({:id => params[:blob],
'bucket' => params[:bucket]})
respond_to do |format|
format.xml { haml :"blobs/show", :locals => { :blob => @blob } }
@@ -239,7 +239,7 @@ module Deltacloud::Collections
content_type = env['CONTENT_TYPE'] || ""
blob_data = {:tempfile => temp_file, :type => content_type}
user_meta = BlobHelper::extract_blob_metadata_hash(request.env)
- @blob = driver.create_blob(credentials, bucket_id, blob_id, blob_data, user_meta)
+ @blob = driver.create_blob(bucket_id, blob_id, blob_data, user_meta)
temp_file.delete
respond_to do |format|
format.xml { haml :"blobs/show", :locals => { :blob => @blob } }
@@ -255,7 +255,7 @@ module Deltacloud::Collections
param :blob_id, :string, :required
control do
@blob_id = params[:blob_id]
- @blob_metadata = driver.blob_metadata(credentials, {:id => params[:blob_id], 'bucket' => params[:id]})
+ @blob_metadata = driver.blob_metadata({:id => params[:blob_id], 'bucket' => params[:id]})
if @blob_metadata
@blob_metadata.each do |k,v|
headers["X-Deltacloud-Blobmeta-#{k}"] = v
@@ -275,7 +275,7 @@ module Deltacloud::Collections
param :blob_id, :string, :required
control do
meta_hash = BlobHelper::extract_blob_metadata_hash(request.env)
- success = driver.update_blob_metadata(credentials, {'bucket'=>params[:id], :id =>params[:blob_id], 'meta_hash' => meta_hash})
+ success = driver.update_blob_metadata({'bucket'=>params[:id], :id =>params[:blob_id], 'meta_hash' => meta_hash})
if(success)
meta_hash.each do |k,v|
headers["X-Deltacloud-Blobmeta-#{k}"] = v
@@ -296,12 +296,12 @@ module Deltacloud::Collections
param :id, :string, :required
param :blob_id, :string, :required
control do
- @blob = driver.blob(credentials, { :id => params[:blob_id], 'bucket' => params[:id]})
+ @blob = driver.blob({ :id => params[:blob_id], 'bucket' => params[:id]})
if @blob
params['content_length'] = @blob.content_length
params['content_type'] = (@blob.content_type.nil? || @blob.content_type == "")? "application/octet-stream" : @blob.content_type
params['content_disposition'] = "attachment; filename=#{@blob.id}"
- BlobStream.call(self, credentials, params)
+ BlobStream.call(self, params)
else
report_error(404)
end
diff --git a/server/lib/deltacloud/collections/drivers.rb b/server/lib/deltacloud/collections/drivers.rb
index c49ab43..110f3d2 100644
--- a/server/lib/deltacloud/collections/drivers.rb
+++ b/server/lib/deltacloud/collections/drivers.rb
@@ -33,7 +33,7 @@ module Deltacloud::Collections
control do
@name = params[:id].to_sym
if driver_symbol == @name
- @providers = driver.providers(credentials) if driver.respond_to? :providers
+ @providers = driver.providers if driver.respond_to? :providers
end
@driver = Deltacloud::Drivers.driver_config[@name]
halt 404 unless @driver
diff --git a/server/lib/deltacloud/collections/firewalls.rb b/server/lib/deltacloud/collections/firewalls.rb
index e6ae4ee..8707be1 100644
--- a/server/lib/deltacloud/collections/firewalls.rb
+++ b/server/lib/deltacloud/collections/firewalls.rb
@@ -20,7 +20,7 @@ module Deltacloud::Collections
set :capability, lambda { |m| driver.respond_to? m }
- check_features :for => lambda { |c, f| driver.class.has_feature?(c, f) }
+ check_features :for => lambda { |c, f| driver.has_feature?(c, f) }
get '/firewalls/:id/new_rule' do
@firewall_name = params[:id]
@@ -41,7 +41,7 @@ module Deltacloud::Collections
opts = {}
opts[:firewall] = params[:id]
opts[:rule_id] = params[:rule_id]
- driver.delete_firewall_rule(credentials, opts)
+ driver.delete_firewall_rule(opts)
status 204
respond_to do |format|
format.xml
@@ -60,7 +60,7 @@ module Deltacloud::Collections
param :name, :string, :required
param :description, :string, :required
control do
- @firewall = driver.create_firewall(credentials, params )
+ @firewall = driver.create_firewall(params )
status 201 # Created
response['Location'] = firewall_url(@firewall.id)
respond_to do |format|
@@ -73,7 +73,7 @@ module Deltacloud::Collections
operation :destroy, :with_capability => :delete_firewall do
control do
- driver.delete_firewall(credentials, params)
+ driver.delete_firewall(params)
status 204
respond_to do |format|
format.xml
@@ -103,8 +103,8 @@ module Deltacloud::Collections
400, 'No sources. Specify at least one source ip address or group.'
)
end
- driver.create_firewall_rule(credentials, params)
- @firewall = driver.firewall(credentials, {:id => params[:id]})
+ driver.create_firewall_rule(params)
+ @firewall = driver.firewall({:id => params[:id]})
status 201
respond_to do |format|
format.xml { haml :"firewalls/show", :locals => { :firewall => @firewall} }
diff --git a/server/lib/deltacloud/collections/hardware_profiles.rb b/server/lib/deltacloud/collections/hardware_profiles.rb
index d5bf400..68a59c8 100644
--- a/server/lib/deltacloud/collections/hardware_profiles.rb
+++ b/server/lib/deltacloud/collections/hardware_profiles.rb
@@ -16,7 +16,7 @@
module Deltacloud::Collections
class HardwareProfiles < Base
- set :capability, lambda { |m| !driver.class.send(m).empty? }
+ set :capability, lambda { |m| !driver.send(m).empty? }
collection :hardware_profiles do
diff --git a/server/lib/deltacloud/collections/images.rb b/server/lib/deltacloud/collections/images.rb
index 3d59df5..1dde99d 100644
--- a/server/lib/deltacloud/collections/images.rb
+++ b/server/lib/deltacloud/collections/images.rb
@@ -18,7 +18,7 @@ module Deltacloud::Collections
include Deltacloud::Features
- check_features :for => lambda { |c, f| driver.class.has_feature?(c, f) }
+ check_features :for => lambda { |c, f| driver.has_feature?(c, f) }
set :capability, lambda { |m| driver.respond_to? m }
new_route_for :images do
@@ -41,7 +41,7 @@ module Deltacloud::Collections
operation :create, :with_capability => :create_image do
param :instance_id, :string, :required
control do
- @image = driver.create_image(credentials, {
+ @image = driver.create_image({
:id => params[:instance_id],
:name => params[:name],
:description => params[:description]
@@ -58,7 +58,7 @@ module Deltacloud::Collections
operation :destroy, :with_capability => :destroy_image do
control do
- driver.destroy_image(credentials, params[:id])
+ driver.destroy_image(params[:id])
respond_to do |format|
format.xml { status 204 }
format.json { status 204 }
diff --git a/server/lib/deltacloud/collections/instances.rb b/server/lib/deltacloud/collections/instances.rb
index 6038bee..a50d02d 100644
--- a/server/lib/deltacloud/collections/instances.rb
+++ b/server/lib/deltacloud/collections/instances.rb
@@ -19,30 +19,30 @@ module Deltacloud::Collections
include Deltacloud::Features
set :capability, lambda { |m| driver.respond_to? m }
- check_features :for => lambda { |c, f| driver.class.has_feature?(c, f) }
+ check_features :for => lambda { |c, f| driver.has_feature?(c, f) }
new_route_for(:instances) do
@opts = {
:instance => Deltacloud::Instance.new(:id=>params[:id], :image_id=>params[:image_id]),
}
- @opts[:image] = driver.image(credentials, :id => params[:image_id])
+ @opts[:image] = driver.image(params[:image_id])
@opts[:hardware_profiles] = @opts[:image].hardware_profiles
if params[:realm_id]
@opts[:realms] = [ Deltacloud::Realm.new(:id => params[:realm_id]) ] if params[:realm_id]
else
- @opts[:realms] = driver.realms(credentials)
+ @opts[:realms] = driver.realms
end
- if driver.class.has_feature?(:instances, :firewalls)
- @opts[:firewalls] = driver.firewalls(credentials)
+ if driver.has_feature?(:instances, :firewalls)
+ @opts[:firewalls] = driver.firewalls
end
- if driver.class.has_feature?(:instances, :authentication_key)
- @opts[:keys] = driver.keys(credentials)
+ if driver.has_feature?(:instances, :authentication_key)
+ @opts[:keys] = driver.keys
end
end
get '/instances/:id/run' do
respond_to do |format|
- @instance = driver.instances(credentials, :id => params[:id]).first
+ @instance = driver.instances(:id => params[:id]).first
format.html {haml :"instances/run_command", :locals => @instance }
end
end
@@ -58,7 +58,7 @@ module Deltacloud::Collections
param :hwp_id, :string, :optional
param :keyname, :string, :optional
control do
- @instance = driver.create_instance(credentials, params[:image_id], params)
+ @instance = driver.create_instance(params[:image_id], params)
if @instance.kind_of? Array
@elements = @instance
action_handler = "index"
@@ -120,7 +120,7 @@ module Deltacloud::Collections
param :ip, :string, :optional, [], "IP address of target instance"
param :port, :string, :optional, ['22'], "Target port"
control do
- @output = driver.run_on_instance(credentials, params)
+ @output = driver.run_on_instance(params)
respond_to do |format|
format.xml { haml :"instances/run", :locals => { :output => @output } }
format.html { haml :"instances/run", :locals => { :output => @output } }
diff --git a/server/lib/deltacloud/collections/keys.rb b/server/lib/deltacloud/collections/keys.rb
index d1b5541..947f1c2 100644
--- a/server/lib/deltacloud/collections/keys.rb
+++ b/server/lib/deltacloud/collections/keys.rb
@@ -19,7 +19,7 @@ module Deltacloud::Collections
include Deltacloud::Features
set :capability, lambda { |m| driver.respond_to? m }
- check_features :for => lambda { |c, f| driver.class.has_feature?(c, f) }
+ check_features :for => lambda { |c, f| driver.has_feature?(c, f) }
get '/keys/new' do
respond_to do |format|
@@ -36,7 +36,7 @@ module Deltacloud::Collections
param :name, :string, :required
param :public_key, :string, :optional
control do
- @key = driver.create_key(credentials, { :key_name => params[:name], :public_key => params[:public_key]})
+ @key = driver.create_key({ :key_name => params[:name], :public_key => params[:public_key]})
status 201
response['Location'] = key_url(@key.id)
respond_to do |format|
@@ -49,7 +49,7 @@ module Deltacloud::Collections
operation :destroy, :with_capability => :destroy_key do
control do
- driver.destroy_key(credentials, { :id => params[:id]})
+ driver.destroy_key({ :id => params[:id]})
status 204
respond_to do |format|
format.xml
diff --git a/server/lib/deltacloud/collections/load_balancers.rb b/server/lib/deltacloud/collections/load_balancers.rb
index c08cfad..fc93490 100644
--- a/server/lib/deltacloud/collections/load_balancers.rb
+++ b/server/lib/deltacloud/collections/load_balancers.rb
@@ -19,7 +19,7 @@ module Deltacloud::Collections
set :capability, lambda { |m| driver.respond_to? m }
new_route_for :load_balancers do
- @opts[:realms] = driver.realms(credentials)
+ @opts[:realms] = driver.realms
end
collection :load_balancers do
@@ -31,15 +31,15 @@ module Deltacloud::Collections
param :id, :string, :required
control do
vars = {}
- vars[:load_balancer] = driver.load_balancer(credentials, params)
+ vars[:load_balancer] = driver.load_balancer(params)
vars[:registered_instances] = vars[:load_balancer].instances.map{ |i| {:id => i.id, :name=> i.name} }
# If provider supports realm_filter and load balancer has only one realm (which is mostly the case), use optimization:
- if vars[:load_balancer].realms.size == 1 and driver.class.has_feature?(:instances, :realm_filter)
- all_instances = driver.instances(credentials, :realm_id => vars[:load_balancer].realms.first.id).collect{ |i|
+ if vars[:load_balancer].realms.size == 1 and driver.has_feature?(:instances, :realm_filter)
+ all_instances = driver.instances(:realm_id => vars[:load_balancer].realms.first.id).collect{ |i|
{ :id => i.id, :name => i.name }
}
else
- all_instances = driver.instances(credentials).collect{|i| {:id => i.id, :name => i.name} }
+ all_instances = driver.instances.collect{|i| {:id => i.id, :name => i.name} }
end
vars[:unregistered_instances] = all_instances - vars[:registered_instances]
respond_to do |format|
@@ -57,7 +57,7 @@ module Deltacloud::Collections
param :listener_balancer_port, :string, :required
param :listener_instance_port, :string, :required
control do
- @load_balancer = driver.create_load_balancer(credentials, params)
+ @load_balancer = driver.create_load_balancer(params)
status 201 # Created
response['Location'] = load_balancer_url(@load_balancer.id)
respond_to do |format|
@@ -72,8 +72,8 @@ module Deltacloud::Collections
param :id, :string, :required
param :instance_id, :string, :required
control do
- driver.lb_register_instance(credentials, params)
- @load_balancer = driver.load_balancer(credentials, :id => params[:id])
+ driver.lb_register_instance(params)
+ @load_balancer = driver.load_balancer(:id => params[:id])
respond_to do |format|
format.xml { haml :'load_balancers/show', :locals => { :load_balancer => @load_balancer } }
format.json { JSON::dump(:load_balancer => @load_balancer.to_hash(self) ) }
@@ -86,8 +86,8 @@ module Deltacloud::Collections
param :id, :string, :required
param :instance_id, :string, :required
control do
- driver.lb_unregister_instance(credentials, params)
- @load_balancer = driver.load_balancer(credentials, :id => params[:id])
+ driver.lb_unregister_instance(params)
+ @load_balancer = driver.load_balancer(:id => params[:id])
respond_to do |format|
format.xml { haml :'load_balancers/show', :locals => { :load_balancer => @load_balancer } }
format.json { JSON::dump(:load_balancer => @load_balancer.to_hash(self)) }
@@ -98,7 +98,7 @@ module Deltacloud::Collections
operation :destroy, :with_capability => :destroy_load_balancer do
control do
- driver.destroy_load_balancer(credentials, params[:id])
+ driver.destroy_load_balancer(params[:id])
status 204
respond_to do |format|
format.xml
diff --git a/server/lib/deltacloud/collections/storage_snapshots.rb b/server/lib/deltacloud/collections/storage_snapshots.rb
index c9836fa..8832c47 100644
--- a/server/lib/deltacloud/collections/storage_snapshots.rb
+++ b/server/lib/deltacloud/collections/storage_snapshots.rb
@@ -17,7 +17,7 @@ module Deltacloud::Collections
class StorageSnapshots < Base
set :capability, lambda { |m| driver.respond_to? m }
- check_features :for => lambda { |c, f| driver.class.has_feature?(c, f) }
+ check_features :for => lambda { |c, f| driver.has_feature?(c, f) }
new_route_for(:storage_snapshots)
@@ -30,7 +30,7 @@ module Deltacloud::Collections
param :name, :string, :optional
param :description, :string, :optional
control do
- @storage_snapshot = driver.create_storage_snapshot(credentials, params)
+ @storage_snapshot = driver.create_storage_snapshot(params)
status 201 # Created
response['Location'] = storage_snapshot_url(@storage_snapshot.id)
respond_to do |format|
@@ -43,7 +43,7 @@ module Deltacloud::Collections
operation :destroy, :with_capability => :destroy_storage_snapshot do
control do
- driver.destroy_storage_snapshot(credentials, params)
+ driver.destroy_storage_snapshot(params)
status 204
respond_to do |format|
format.xml
diff --git a/server/lib/deltacloud/collections/storage_volumes.rb b/server/lib/deltacloud/collections/storage_volumes.rb
index b3a2958..1e76b03 100644
--- a/server/lib/deltacloud/collections/storage_volumes.rb
+++ b/server/lib/deltacloud/collections/storage_volumes.rb
@@ -19,12 +19,12 @@ module Deltacloud::Collections
include Deltacloud::Features
set :capability, lambda { |m| driver.respond_to? m }
- check_features :for => lambda { |c, f| driver.class.has_feature?(c, f) }
+ check_features :for => lambda { |c, f| driver.has_feature?(c, f) }
new_route_for(:storage_volumes)
get "/storage_volumes/:id/attach_instance" do
- @opts[:instances] = driver.instances(credentials)
+ @opts[:instances] = driver.instances
respond_to do |format|
format.html{ haml :"storage_volumes/attach"}
end
@@ -42,7 +42,7 @@ module Deltacloud::Collections
param :name, :string, :optional
param :description, :string, :optional
control do
- @storage_volume = driver.create_storage_volume(credentials, params)
+ @storage_volume = driver.create_storage_volume(params)
status 201
response['Location'] = storage_volume_url(@storage_volume.id)
respond_to do |format|
@@ -58,7 +58,7 @@ module Deltacloud::Collections
param :instance_id,:string, :required
param :device, :string, :required
control do
- @storage_volume = driver.attach_storage_volume(credentials, params)
+ @storage_volume = driver.attach_storage_volume(params)
status 202
respond_to do |format|
format.html { redirect(storage_volume_url(params[:id]))}
@@ -71,8 +71,8 @@ module Deltacloud::Collections
action :detach, :with_capability => :detach_storage_volume do
param :id, :string, :required
control do
- volume = driver.storage_volume(credentials, :id => params[:id])
- @storage_volume = driver.detach_storage_volume(credentials, :id => volume.id,
+ volume = driver.storage_volume(:id => params[:id])
+ @storage_volume = driver.detach_storage_volume(:id => volume.id,
:instance_id => volume.instance_id,
:device => volume.device)
status 202
@@ -86,7 +86,7 @@ module Deltacloud::Collections
operation :destroy, :with_capability => :destroy_storage_volume do
control do
- driver.destroy_storage_volume(credentials, params)
+ driver.destroy_storage_volume(params)
status 204
respond_to do |format|
format.xml
diff --git a/server/lib/deltacloud/drivers/base_driver.rb b/server/lib/deltacloud/drivers/base_driver.rb
index 8e1c6df..d408d1c 100644
--- a/server/lib/deltacloud/drivers/base_driver.rb
+++ b/server/lib/deltacloud/drivers/base_driver.rb
@@ -155,23 +155,6 @@ module Deltacloud
(self.class.instance_methods - superclass_methods).include? method
end
- def supported_collections(credentials)
- collection_arr = []
- Deltacloud::Collections.modules(:deltacloud).each do |m|
- m.collections.each do |c|
- # Get the required capability for the :index operation (like 'realms' or 'instance_state_machine')
- index_operation_capability = c.operation(:index).required_capability
- # Then use this capability to check if the 'capability' lambda defined
- # for the Sinatra::Base class evaluate to 'true'
- next if m.settings.respond_to?(:capability) and !m.settings.capability(index_operation_capability)
- yield c if block_given?
- collection_arr << c
- end
- end
- collection_arr
- end
-
-
## Capabilities
# The rabbit dsl supports declaring a capability that is required
# in the backend driver for the call to succeed. A driver can
diff --git a/server/lib/deltacloud/drivers/mock/mock_driver.rb b/server/lib/deltacloud/drivers/mock/mock_driver.rb
index feb3a01..d3c72be 100644
--- a/server/lib/deltacloud/drivers/mock/mock_driver.rb
+++ b/server/lib/deltacloud/drivers/mock/mock_driver.rb
@@ -20,6 +20,7 @@ require_relative 'mock_client'
require_relative 'mock_driver_cimi_methods'
require_relative '../../runner'
+require_relative '../../../initializers/mock_initialize.rb'
module Deltacloud::Drivers::Mock
@@ -257,7 +258,7 @@ module Deltacloud::Drivers::Mock
inst = @client.load_collection(:instances, id)
inst[:network_interfaces].each do |network_interface|
destroy_network_interface(credentials, network_interface)
- end
+ end if inst[:network_interfaces]
@client.destroy(:instances, id)
end
diff --git a/server/lib/deltacloud/helpers/deltacloud_helper.rb b/server/lib/deltacloud/helpers/deltacloud_helper.rb
index 242effc..79345df 100644
--- a/server/lib/deltacloud/helpers/deltacloud_helper.rb
+++ b/server/lib/deltacloud/helpers/deltacloud_helper.rb
@@ -31,7 +31,7 @@ module Deltacloud::Helpers
{
:rel => c.collection_name,
:href => self.send(:"#{c.collection_name}_url"),
- :features => c.features.select { |f| driver.class.has_feature?(c.collection_name, f.name) }.map { |f|
+ :features => c.features.select { |f| driver.has_feature?(c.collection_name, f.name) }.map { |f|
f.operations.map { |o|
{ :name => f.name, :rel => o.name, :params => o.params_array, :constraints => constraints_hash_for(c.collection_name, f.name) }
}
@@ -52,8 +52,8 @@ module Deltacloud::Helpers
end
def auth_feature_name
- return 'key' if driver.class.has_feature?(:instances, :authentication_key)
- return 'password' if driver.class.has_feature?(:instances, :authentication_password)
+ return 'key' if driver.has_feature?(:instances, :authentication_key)
+ return 'password' if driver.has_feature?(:instances, :authentication_password)
end
def instance_action_method(action)
@@ -67,7 +67,7 @@ module Deltacloud::Helpers
def filter_all(model, opts={})
begin
- @benchmark = Benchmark.measure { @elements = driver.send(model.to_sym, credentials, params) }
+ @benchmark = Benchmark.measure { @elements = driver.send(model.to_sym, params) }
rescue => e
@exception = e
end
@@ -87,7 +87,7 @@ module Deltacloud::Helpers
def show(model, opts={})
@benchmark = Benchmark.measure do
- @element = driver.send(model, credentials, { :id => params[:id]} )
+ @element = driver.send(model, params[:id])
end
headers['X-Backend-Runtime'] = @benchmark.real.to_s
instance_variable_set("@#{model}", @element)
@@ -146,7 +146,7 @@ module Deltacloud::Helpers
end
def instance_action(name)
- unless original_instance = driver.instance(credentials, :id => params[:id])
+ unless original_instance = driver.instance(:id => params[:id])
return report_error(403)
end
@@ -157,7 +157,7 @@ module Deltacloud::Helpers
end
@benchmark = Benchmark.measure do
- @instance = driver.send(:"#{name}_instance", credentials, params[:id])
+ @instance = driver.send(:"#{name}_instance", params[:id])
end
headers['X-Backend-Runtime'] = @benchmark.real.to_s
@@ -319,7 +319,7 @@ module Deltacloud::Helpers
end
def additional_features_for?(collection_name, apart_from = [])
- features_arr = (driver.class.features[collection_name.to_sym] || [] ) - apart_from
+ features_arr = (driver.features[collection_name.to_sym] || [] ) - apart_from
not features_arr.empty?
end
diff --git a/server/lib/deltacloud/helpers/driver_helper.rb b/server/lib/deltacloud/helpers/driver_helper.rb
index f235a0f..d53f65b 100644
--- a/server/lib/deltacloud/helpers/driver_helper.rb
+++ b/server/lib/deltacloud/helpers/driver_helper.rb
@@ -16,6 +16,10 @@
module Deltacloud::Helpers
module Drivers
+ require 'deltacloud/api'
+
+ require_relative 'auth_helper'
+ include Sinatra::AuthHelper
# This will make the Driver helpers, like 'driver'
# accessible on class level
@@ -36,27 +40,17 @@ module Deltacloud::Helpers
Thread.current[:provider] || ENV['API_PROVIDER']
end
- def driver_class_name
- driver_name.camelize
- end
-
- def driver_source_name
- File.join('..', 'drivers', driver_name, driver_name + '_driver.rb')
- end
-
- def driver_class
- m = Deltacloud::Drivers.const_get(driver_class_name)
- m.const_get(driver_class_name + "Driver").new
- end
-
def driver
- begin
- driver_class
- rescue NameError => e
- raise "Driver '#{driver_name}' is not available (#{e.message})."
+ unless Thread.current[:api_driver]
+ ## FIXME: credentials are defined in Sinatra::AuthHelper - solve dependency
+ Thread.current[:api_driver] = Deltacloud.new(
+ driver_symbol,
+ :user => Thread.current[:user],
+ :password => Thread.current[:password]
+ )
end
+ Thread.current[:api_driver]
end
-
end
end
diff --git a/server/lib/deltacloud/models/instance.rb b/server/lib/deltacloud/models/instance.rb
index b435387..989f084 100644
--- a/server/lib/deltacloud/models/instance.rb
+++ b/server/lib/deltacloud/models/instance.rb
@@ -73,11 +73,11 @@ module Deltacloud
if storage_volumes
r.merge!(:storage_volumes => storage_volumes.map { |v| { :id => v.keys.first, :href => context.storage_volume_url(v.keys.first), :rel => :storage_volume }})
end
- if context.driver.class.has_feature?(:instances, :authentication_key)
+ if context.driver.has_feature?(:instances, :authentication_key)
r.merge!(:authentication_type => 'key' )
r.merge!(:authentication => { :keyname => keyname }) if keyname
end
- if context.driver.class.has_feature?(:instances, :authentication_password)
+ if context.driver.has_feature?(:instances, :authentication_password)
r[:authentication] && username ? r[:authentication].merge!({ :user => username, :password => password }) :
(username ? r.merge!(:authentication => { :user => username, :password => password }) : nil)
end
diff --git a/server/lib/deltacloud/server.rb b/server/lib/deltacloud/server.rb
index 6c39e3a..df8d86f 100644
--- a/server/lib/deltacloud/server.rb
+++ b/server/lib/deltacloud/server.rb
@@ -46,7 +46,7 @@ module Deltacloud
if params[:force_auth]
return [401, 'Authentication failed'] unless driver.valid_credentials?(credentials)
end
- collections = driver.supported_collections(credentials)
+ collections = supported_collections
respond_to do |format|
format.xml { haml :"api/show", :locals => { :collections => collections } }
format.json { collections_to_json(collections) }
@@ -55,7 +55,7 @@ module Deltacloud
end
options '/' do
- headers 'Allow' => driver.supported_collections(credentials) { |c| c.collection_name }.join(',')
+ headers 'Allow' => supported_collections { |c| c.collection_name }.join(',')
end
post '/' do
@@ -73,6 +73,23 @@ module Deltacloud
end
end
+ private
+
+ def supported_collections
+ collection_arr = []
+ Deltacloud::Collections.modules(:deltacloud).each do |m|
+ m.collections.each do |c|
+ # Get the required capability for the :index operation (like 'realms' or 'instance_state_machine')
+ index_operation_capability = c.operation(:index).required_capability
+ # Then use this capability to check if the 'capability' lambda defined
+ # for the Sinatra::Base class evaluate to 'true'
+ next if m.settings.respond_to?(:capability) and !m.settings.capability(index_operation_capability)
+ yield c if block_given?
+ collection_arr << c
+ end
+ end
+ collection_arr
+ end
end
end
diff --git a/server/views/api/show.xml.haml b/server/views/api/show.xml.haml
index 852a599..1366d4e 100644
--- a/server/views/api/show.xml.haml
+++ b/server/views/api/show.xml.haml
@@ -1,7 +1,7 @@
%api{ :version => settings.version, :driver => driver_symbol, :provider => Thread.current[:provider] || ENV['API_PROVIDER'] }
- collections.each do |c|
%link{ :rel => c.collection_name, :href => self.send(:"#{c.collection_name}_url")}
- - c.features.select { |f| driver.class.has_feature?(c.collection_name, f.name) }.each do |f|
+ - c.features.select { |f| driver.has_feature?(c.collection_name, f.name) }.each do |f|
- f.operations.each do |operation|
%feature{ :name => f.name, :rel => operation.name }
- operation.params_array.each do |param|
diff --git a/server/views/buckets/new.html.haml b/server/views/buckets/new.html.haml
index 45b9ae3..992d091 100644
--- a/server/views/buckets/new.html.haml
+++ b/server/views/buckets/new.html.haml
@@ -6,7 +6,7 @@
Bucket Name:
%input{ :name => 'name', :size => 250}/
%br
- -if driver.class.has_feature?(:buckets, :bucket_location)
+ -if driver.has_feature?(:buckets, :bucket_location)
%p
%label
Location: (optional)
diff --git a/server/views/instances/new.html.haml b/server/views/instances/new.html.haml
index 57ce572..ccd5c31 100644
--- a/server/views/instances/new.html.haml
+++ b/server/views/instances/new.html.haml
@@ -21,7 +21,7 @@
%form{ :action => instances_url, :method => :post, :class => :new_instance, :enctype => 'multipart/form-data', :'data-ajax' => 'false'}
%input{ :name => :image_id, :type => :hidden, :value => instance.image_id }/
- - if driver.class.has_feature?(:instances, :user_name)
+ - if driver.has_feature?(:instances, :user_name)
%div{ 'data-role' => :fieldcontain }
%label{ :for => :name} Instance name:
%input{ :type => :text, :id => :name, :name => :name, :value => '' }
@@ -30,14 +30,14 @@
%div{ 'data-role' => :collapsible, 'data-collapsed' => "true"}
%h3 Additional parameters
- - if driver.class.has_feature?(:instances, :user_data)
+ - if driver.has_feature?(:instances, :user_data)
%div{ 'data-role' => :fieldcontain }
%label{ :for => :user_data} Base64 encoded user-data:
%textarea{ :id => :user_data, :name => :user_data, :value => '' }
%br/
%a{ :href => "", :onclick => 'encodeb64();', :'data-ajax' => 'false'} Encode data
- - if driver.class.has_feature?(:instances, :first_boot_dev)
+ - if driver.has_feature?(:instances, :first_boot_dev)
%fieldset{ :'data-role' => 'controlgroup', 'data-iconpos' => 'right'}
%legend First boot device:
%input{ :name => 'first_boot_dev', :id => :first_boot_dev_hd, :value => 'hd', :checked => 'checked', :type => 'radio'}
@@ -45,17 +45,17 @@
%input{ :name => 'first_boot_dev', :id => :first_boot_dev_network, :value => 'network', :type => "radio"}
%label{ :for => :first_boot_dev_network} Network
- - if driver.class.has_feature?(:instances, :instance_count)
+ - if driver.has_feature?(:instances, :instance_count)
%div{ 'data-role' => :fieldcontain }
%label{ :for => :instance_count} # of instances to be launched:
%input{ :type => :text, :id => :instance_count, :name => :instance_count, :value => '1' }
- - if driver.class.has_feature?(:instances, :metrics)
+ - if driver.has_feature?(:instances, :metrics)
%div{ 'data-role' => :fieldcontain }
%label{ :for => :metrics} Enable metrics for this instance
%input{ :type => :checkbox, :id => :metrics, :name => :metrics, :checked => 'false' }
- - if driver.class.has_feature?(:instances, :authentication_key)
+ - if driver.has_feature?(:instances, :authentication_key)
%div{ 'data-role' => :fieldcontain }
%label{ :for => :keyname, :class => 'ui-input-text'} Instance SSH key:
%select{:name => 'keyname', :'data-native-menu' => "true" }
@@ -63,7 +63,7 @@
- keys.each do |key|
%option{ :value => key.id } #{key.id}
- - if driver.class.has_feature?(:instances, :register_to_load_balancer)
+ - if driver.has_feature?(:instances, :register_to_load_balancer)
%div{ 'data-role' => :fieldcontain }
%label{ :for => :load_balancer_id, :class => 'ui-input-text'} Register to loadbalancer:
%select{:name => 'load_balancer_id', :'data-native-menu' => "true" }
@@ -71,7 +71,7 @@
- load_balancers.each do |load_balancer|
%option{:value => load_balancer.id} #{load_balancer.id}
- - if driver.class.has_feature?(:instances, :firewalls)
+ - if driver.has_feature?(:instances, :firewalls)
%div{ 'data-role' => :fieldcontain }
%fieldset{ :'data-role' => 'controlgroup'}
%legend Register to firewall:
@@ -83,7 +83,7 @@
%input{:type => :checkbox, :value => firewalls[i].name, :name => "firewalls#{i}", :id => "firewalls#{i}"}/
%label{:for => "firewalls#{i}"}=firewalls[i].name
- - if driver.class.has_feature?(:instances, :user_files)
+ - if driver.has_feature?(:instances, :user_files)
%div{ 'data-role' => :fieldcontain }
%h3 Make user data file(s) available in launched Instance
%h4 The path specified for each file below is where the data will be made available on the launched Instance.
diff --git a/server/views/instances/show.xml.haml b/server/views/instances/show.xml.haml
index 220a0a8..21bfdad 100644
--- a/server/views/instances/show.xml.haml
+++ b/server/views/instances/show.xml.haml
@@ -45,7 +45,7 @@
%storage_volumes<
- instance.storage_volumes.each do |volume|
%storage_volume{:href=> storage_volume_url(volume.keys.first), :id => volume.keys.first, :device => volume.values.first}
- - if driver.class.has_feature?(:instances, :authentication_key) or driver.class.has_feature?(:instances, :authentication_password)
+ - if driver.has_feature?(:instances, :authentication_key) or driver.has_feature?(:instances, :authentication_password)
%authentication{ :type => auth_feature_name }
- if instance.authn_feature_failed?
%error #{instance.authn_error}
--
1.7.11.7