You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@deltacloud.apache.org by mf...@redhat.com on 2012/05/15 17:15:32 UTC
Modular Deltacloud (rev 2)
Hi,
I tried hard to shrink the patches and ended up with number 21 ;-)
I hope that will make it more easy to review.
This revision also feature fixed cucumber and mock unit tests:
$ cd core/server
$ rake cucumber:mock:test
$ rake test:mock
The EC2 version of Cucumber should work too, but I have some strange
problem with VCR I need to figure out.
-- Michal
[PATCH core 03/26] Core: Replaced obsolete or unused Deltacloud code
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/lib/deltacloud/backend_capability.rb | 42 --
server/lib/deltacloud/base_driver.rb | 18 -
server/lib/deltacloud/base_driver/mock_driver.rb | 78 ----
server/lib/deltacloud/collections/instances.rb | 3 +
server/lib/deltacloud/core_ext.rb | 21 -
.../lib/deltacloud/drivers/ec2/ec2_mock_driver.rb | 186 ---------
server/lib/deltacloud/hardware_profile.rb | 192 ---------
server/lib/deltacloud/helpers.rb | 2 -
.../lib/deltacloud/helpers/application_helper.rb | 238 -----------
server/lib/deltacloud/helpers/auth_helper.rb | 73 ++++
server/lib/deltacloud/helpers/blob_stream.rb | 213 ----------
.../lib/deltacloud/helpers/blob_stream_helper.rb | 214 ++++++++++
server/lib/deltacloud/helpers/conversion_helper.rb | 43 --
server/lib/deltacloud/helpers/deltacloud_helper.rb | 273 ++++++++++++
server/lib/deltacloud/helpers/driver_helper.rb | 57 +++
.../deltacloud/helpers/hardware_profiles_helper.rb | 50 ---
server/lib/deltacloud/helpers/json_helper.rb | 31 --
server/lib/deltacloud/helpers/rabbit_helper.rb | 34 ++
server/lib/deltacloud/helpers/url_helper.rb | 112 +++++
server/lib/deltacloud/method_serializer.rb | 83 ----
server/lib/deltacloud/models/instance.rb | 2 -
server/lib/deltacloud/models/key.rb | 4 +
server/lib/deltacloud/validation.rb | 100 -----
server/lib/sinatra/lazy_auth.rb | 75 ----
server/lib/sinatra/rabbit.rb | 441 --------------------
server/lib/sinatra/rack_accept.rb | 4 -
server/lib/sinatra/rack_cimi.rb | 33 --
server/lib/sinatra/rack_matrix_params.rb | 4 +-
server/lib/sinatra/rack_runtime.rb | 47 ---
server/lib/sinatra/rack_syslog.rb | 93 -----
server/lib/sinatra/sinatra_verbose.rb | 73 ----
server/lib/sinatra/static_assets.rb | 99 -----
server/lib/sinatra/url_for.rb | 93 -----
server/views/addresses/show.html.haml | 2 +-
server/views/api/show.html.haml | 7 +-
server/views/error.html.haml | 4 +-
server/views/instances/new.html.haml | 12 +-
server/views/instances/show.xml.haml | 2 +-
server/views/layout.html.haml | 6 +-
server/views/root/index.html.haml | 2 +-
40 files changed, 789 insertions(+), 2277 deletions(-)
delete mode 100644 server/lib/deltacloud/backend_capability.rb
delete mode 100644 server/lib/deltacloud/base_driver.rb
delete mode 100644 server/lib/deltacloud/base_driver/mock_driver.rb
delete mode 100644 server/lib/deltacloud/core_ext.rb
delete mode 100644 server/lib/deltacloud/drivers/ec2/ec2_mock_driver.rb
delete mode 100644 server/lib/deltacloud/hardware_profile.rb
delete mode 100644 server/lib/deltacloud/helpers/application_helper.rb
create mode 100644 server/lib/deltacloud/helpers/auth_helper.rb
delete mode 100644 server/lib/deltacloud/helpers/blob_stream.rb
create mode 100644 server/lib/deltacloud/helpers/blob_stream_helper.rb
delete mode 100644 server/lib/deltacloud/helpers/conversion_helper.rb
create mode 100644 server/lib/deltacloud/helpers/deltacloud_helper.rb
create mode 100644 server/lib/deltacloud/helpers/driver_helper.rb
delete mode 100644 server/lib/deltacloud/helpers/hardware_profiles_helper.rb
delete mode 100644 server/lib/deltacloud/helpers/json_helper.rb
create mode 100644 server/lib/deltacloud/helpers/rabbit_helper.rb
create mode 100644 server/lib/deltacloud/helpers/url_helper.rb
delete mode 100644 server/lib/deltacloud/method_serializer.rb
delete mode 100644 server/lib/deltacloud/validation.rb
delete mode 100644 server/lib/sinatra/lazy_auth.rb
delete mode 100644 server/lib/sinatra/rabbit.rb
delete mode 100644 server/lib/sinatra/rack_cimi.rb
delete mode 100644 server/lib/sinatra/rack_runtime.rb
delete mode 100644 server/lib/sinatra/rack_syslog.rb
delete mode 100644 server/lib/sinatra/sinatra_verbose.rb
delete mode 100644 server/lib/sinatra/static_assets.rb
delete mode 100644 server/lib/sinatra/url_for.rb
diff --git a/server/lib/deltacloud/backend_capability.rb b/server/lib/deltacloud/backend_capability.rb
deleted file mode 100644
index 7f19007..0000000
--- a/server/lib/deltacloud/backend_capability.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# 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::BackendCapability
-
- attr_reader :capability
-
- def with_capability(capability)
- @capability = capability
- end
-
- def has_capability?(backend)
- !capability or backend.has_capability?(capability)
- end
-
- def check_capability(backend)
- if !has_capability?(backend)
- raise Deltacloud::ExceptionHandler::NotSupported.new("#{capability} capability not supported by backend #{backend.class.name}")
- end
- end
-
- module Helpers
- def operations_for_collection(collection)
- collections[collection].operations.values.select { |op| op.has_capability?(driver) }
- end
- end
-
- helpers Helpers
-end
diff --git a/server/lib/deltacloud/base_driver.rb b/server/lib/deltacloud/base_driver.rb
deleted file mode 100644
index 77cdc84..0000000
--- a/server/lib/deltacloud/base_driver.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-#
-# 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.
-
-require 'deltacloud/base_driver/base_driver'
-require 'deltacloud/base_driver/features'
diff --git a/server/lib/deltacloud/base_driver/mock_driver.rb b/server/lib/deltacloud/base_driver/mock_driver.rb
deleted file mode 100644
index 7956281..0000000
--- a/server/lib/deltacloud/base_driver/mock_driver.rb
+++ /dev/null
@@ -1,78 +0,0 @@
-# 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.
-
-require 'deltacloud/method_serializer'
-
-# Create 'mock' version of original driver client/gem:
-
-# Initialize driver and include Deltacloud
-include Deltacloud
-driver
-
-module Mock
-
- class Ec2 < Aws::Ec2
-
- include MethodSerializer::Cache
-
- def self.cached_methods
- [
- :describe_images,
- :describe_images_by_owner,
- :describe_availability_zones,
- :launch_instances,
- :describe_instances,
- :reboot_instances,
- :create_tag,
- :delete_tag,
- :describe_tags,
- :terminate_instances,
- :describe_key_pairs,
- :create_key_pair,
- :delete_key_pair,
- :create_volume,
- :get_console_output,
- :describe_volumes,
- :delete_volume,
- :attach_volume,
- :detach_volume,
- :describe_snapshots,
- :associate_address,
- :try_create_snapshot,
- ]
- end
-
- MethodSerializer::Cache::wrap_methods(self, :cache_dir => File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'tests', 'ec2', 'support'))
- end
-
-end
-
-
-# Replace original client with mock client
-Deltacloud::Drivers::EC2::EC2Driver.class_eval do
- alias_method :original_new_client, :new_client
-
- def new_client(credentials, provider = :ec2)
- auth_credentials = { :access_key_id => credentials.user, :secret_access_key => credentials.password}
- if provider == :elb
- Mock::ELB.new(auth_credentials)
- elsif provider == :s3
- Mock::S3.new(auth_credentials)
- else
- Mock::Ec2.new(auth_credentials[:access_key_id], auth_credentials[:secret_access_key])
- end
- end
-
-end
diff --git a/server/lib/deltacloud/collections/instances.rb b/server/lib/deltacloud/collections/instances.rb
index 5202149..0da94f2 100644
--- a/server/lib/deltacloud/collections/instances.rb
+++ b/server/lib/deltacloud/collections/instances.rb
@@ -27,6 +27,9 @@ module Deltacloud::Collections
@hardware_profiles = driver.hardware_profiles(credentials, :architecture => @image.architecture )
@realms = [Realm.new(:id => params[:realm_id])] if params[:realm_id]
@realms ||= driver.realms(credentials)
+ if driver.class.has_feature? :instances, :authentication_key
+ @keys = driver.keys(credentials)
+ end
end
collection :instances do
diff --git a/server/lib/deltacloud/core_ext.rb b/server/lib/deltacloud/core_ext.rb
deleted file mode 100644
index fedbc18..0000000
--- a/server/lib/deltacloud/core_ext.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-#
-# 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.
-
-require 'deltacloud/core_ext/string'
-require 'deltacloud/core_ext/integer'
-require 'deltacloud/core_ext/hash'
-require 'deltacloud/core_ext/array'
-require 'deltacloud/core_ext/proc'
diff --git a/server/lib/deltacloud/drivers/ec2/ec2_mock_driver.rb b/server/lib/deltacloud/drivers/ec2/ec2_mock_driver.rb
deleted file mode 100644
index 03db2c1..0000000
--- a/server/lib/deltacloud/drivers/ec2/ec2_mock_driver.rb
+++ /dev/null
@@ -1,186 +0,0 @@
-#
-# 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 RightAws
- class MockEc2
-
- def initialize(opts={})
- end
-
- def describe_images(id)
- load_fixtures_for(:images).select { |i| i[:aws_id].eql?(id) }
- end
-
- def describe_images_by_owner(id)
- load_fixtures_for(:images).select { |i| i[:aws_owner].eql?(id) }
- end
-
- def describe_images(opts={})
- load_fixtures_for(:images)
- end
-
- def describe_availability_zones(opts={})
- load_fixtures_for(:realms)
- end
-
- def describe_instances(opts={})
- instances = load_fixtures_for(:instances)
- instances.each_with_index do |instance, i|
- instances[i] = update_delayed_state(instance)
- return [instance] if opts and opts[:id] and instance[:aws_instance_id].eql?(opts[:id])
- end
- update_fixtures_for(:instances, instances)
- instances
- end
-
- def run_instances(image_id, min_count, max_count, group_ids, key_name, user_data='', addressing_type = nil, instance_type = nil, kernel_id = nil, ramdisk_id = nil, availability_zone = nil, block_device_mappings = nil)
-
- instances = load_fixtures_for(:instances)
- image = load_fixtures_for(:images).select { |img| img[:aws_id].eql?(image_id) }.first
-
- if availability_zone
- realm = load_fixtures_for(:realms).select { |realm| realm[:zone_name].eql?(availability_zone) }.first
- else
- realm = load_fixtures_for(:realms).first
- end
-
- raise Exception unless image
- raise Exception unless realm
-
- instance = { }
- instance[:aws_image_id] = image[:aws_id]
- instance[:aws_availability_zone] = realm[:zone_name]
- instance[:aws_instance_type] = instance_type
- instance[:aws_owner] = user_data
- instance[:aws_state] = 'pending'
- instance[:aws_reason] = ''
- instance[:dns_name] = "#{random_dns}-01-C9.usma2.compute.amazonaws.com"
- instance[:private_dns_name] = "#{random_dns}-02-P9.usma2.compute.amazonaws.com"
- instance[:aws_state_code] = "0"
- instance[:aws_key_name] = "staging"
- instance[:aws_kernel_id] = "aki-be3adfd7"
- instance[:aws_ramdisk_id] = "ari-ce34gad7"
- instance[:aws_groups] = ["default"]
- instance[:aws_instance_id] = random_instance_id
- instance[:aws_reservation_id] = "r-aabbccdd"
- instance[:aws_launch_time] = instance_time_format
-
- instances << instance
-
- update_fixtures_for(:instances, instances)
-
- return [instance]
- end
-
-
- def terminate_instances(id)
- update_instance_state(id, 'stopping', '80')
- end
-
- def reboot_instances(id)
- update_instance_state(id, 'pending', '0')
- end
-
- alias :destroy_instance :terminate_instances
-
- def describe_snapshots(opts={})
- load_fixtures_for(:storage_snapshot)
- end
-
- def describe_volumes(opts={})
- load_fixtures_for(:storage_volume)
- end
-
- private
-
- def driver_dir
- File::expand_path(File::join(File::dirname(__FILE__), '../../../../features/support/ec2'))
- end
-
- def fixtures_path
- File::expand_path(File::join(driver_dir, 'fixtures'))
- end
-
- def load_fixtures_for(collection)
- YAML.load_file(File::join(fixtures_path, "#{collection}.yaml"))
- end
-
- def update_fixtures_for(collection, new_data)
- File.open(File::join(fixtures_path, "#{collection}.yaml"), 'w' ) do |out|
- YAML.dump(new_data, out)
- end
- return new_data
- end
-
- def instance_time_format
- DateTime.now.to_s.gsub(/\+(.+)$/, '.000Z')
- end
-
- def random_instance_id
- id_1 = ("%.4s" % Time.now.to_i.to_s.reverse).reverse
- id_2 = ("%.3s" % Time.now.to_i.to_s.reverse)
- "i-#{id_1}f#{id_2}"
- end
-
- def random_dns
- "domU-#{rand(90)+10}-#{rand(90)+10}-#{rand(90)+10}-#{rand(90)+10}"
- end
-
- def update_delayed_state(instance)
- time = DateTime.now - DateTime.parse(instance[:aws_launch_time])
- hours, minutes, seconds, frac = Date.day_fraction_to_time(time)
-
- if (minutes>(rand(2)+1) or hours>0) and instance[:aws_state].eql?('pending')
- instance[:aws_state], instance[:aws_state_code] = 'running', '16'
- end
-
- if (minutes>(rand(1)+1) or hours>0) and instance[:aws_state].eql?('stopping')
- instance[:aws_state], instance[:aws_state_code] = 'stopped', '80'
- end
-
- return instance
- end
-
- def update_instance_state(id, state, state_code)
- instance = describe_instances(:id => id).first
- if instance
- instance[:aws_state], instance[:aws_state_code] = state, state_code
- instance[:aws_launch_time] = instance_time_format
- instances = load_fixtures_for(:instances)
- instances.each_with_index do |inst, i|
- instances[i] = instance if inst[:aws_instance_id].eql?(id)
- end
- update_fixtures_for(:instances, instances)
- return instance
- else
- raise Exception
- end
- end
-
- end
-end
-
-Deltacloud::Drivers::EC2::EC2Driver.class_eval do
- alias_method :original_new_client, :new_client
-
- def new_client(credentials, opts={})
- if credentials.user != 'mockuser' and credentials.password != 'mockpassword'
- raise "AuthFailure"
- end
- RightAws::MockEc2.new
- end
-
-end
diff --git a/server/lib/deltacloud/hardware_profile.rb b/server/lib/deltacloud/hardware_profile.rb
deleted file mode 100644
index 162f7af..0000000
--- a/server/lib/deltacloud/hardware_profile.rb
+++ /dev/null
@@ -1,192 +0,0 @@
-#
-# 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
- class HardwareProfile
-
- UNITS = {
- :memory => "MB",
- :storage => "GB",
- :architecture => "label",
- :cpu => "count"
- }
-
- def self.unit(name)
- UNITS[name]
- end
-
- class Property
- attr_reader :name, :kind, :default
- # kind == :range
- attr_reader :first, :last
- # kind == :enum
- attr_reader :values
- # kind == :fixed
- attr_reader :value
-
- def initialize(name, values, opts = {})
- @name = name
- if values.is_a?(Range)
- @kind = :range
- @first = values.first
- @last = values.last
- @default = values.first
- elsif values.is_a?(Array)
- @kind = :enum
- @values = values
- @default = values.first
- else
- @kind = :fixed
- @value = values
- @default = @value
- end
- @default = opts[:default] if opts[:default]
- end
-
- def unit
- HardwareProfile.unit(name)
- end
-
- def param
- :"hwp_#{name}"
- end
-
- def fixed?
- kind == :fixed
- end
-
- def valid?(v)
- v = convert_property_value_type(v)
- case kind
- # NOTE:
- # Currently we cannot validate fixed values because of UI
- # limitation. In UI we have multiple hwp_* properties which overide
- # each other.
- # Then provider have one 'static' hardware profile and one
- # 'customizable' when user select the static one the UI also send
- # values from the customizable one (which will lead to a validation
- # error because validation algorith will think that client want to
- # overide fixed values.
- #
- # when :fixed then (v == @default.to_s)
- when :fixed then true
- when :range then match_type?(first, v) and (first..last).include?(v)
- when :enum then match_type?(values.first, v) and values.include?(v)
- else false
- end
- end
-
- def to_param
- Validation::Param.new([param, :string, :optional, []])
- end
-
- def include?(v)
- if kind == :fixed
- return v == value
- else
- return values.include?(v)
- end
- end
-
- private
-
- def match_type?(reference, value)
- true if reference.class == value.class
- end
-
- def convert_property_value_type(v)
- return v.to_f if v =~ /(\d+)\.(\d+)/
- return v.to_i if v =~ /(\d+)/
- v.to_s
- end
- end
-
- class << self
- def property(prop)
- define_method(prop) do |*args|
- values, opts, *ignored = *args
- instvar = :"@#{prop}"
- unless values.nil?
- @properties[prop] = Property.new(prop, values, opts || {})
- end
- @properties[prop]
- end
- end
- end
-
- attr_reader :name
- property :cpu
- property :architecture
- property :memory
- property :storage
-
- def initialize(name,&block)
- @properties = {}
- @name = name
- instance_eval &block if block_given?
- end
-
- def each_property(&block)
- @properties.each_value { |prop| yield prop }
- end
-
- def properties
- @properties.values
- end
-
- def property(name)
- @properties[name.to_sym]
- end
-
- def default?(prop, v)
- p = @properties[prop.to_sym]
- p && p.default.to_s == v
- end
-
- def to_hash
- props = []
- self.each_property do |p|
- if p.kind.eql? :fixed
- props << { :kind => p.kind, :value => p.value, :name => p.name, :unit => p.unit }
- else
- param = { :operation => "create", :method => "post", :name => p.name }
- if p.kind.eql? :range
- param[:range] = { :first => p.first, :last => p.last }
- elsif p.kind.eql? :enum
- param[:enum] = p.values.collect { |v| { :entry => v } }
- end
- param
- props << { :kind => p.kind, :value => p.default, :name => p.name, :unit => p.unit, :param => param }
- end
- end
- {
- :id => self.name,
- :properties => props
- }
- end
-
- def include?(prop, v)
- p = @properties[prop]
- p.nil? || p.include?(v)
- end
-
- def params
- @properties.values.inject([]) { |m, prop|
- m << prop.to_param
- }.compact
- end
- end
-end
diff --git a/server/lib/deltacloud/helpers.rb b/server/lib/deltacloud/helpers.rb
index 73f79ec..dc382bd 100644
--- a/server/lib/deltacloud/helpers.rb
+++ b/server/lib/deltacloud/helpers.rb
@@ -17,7 +17,6 @@
require_relative 'helpers/driver_helper'
require_relative 'helpers/auth_helper'
require_relative 'helpers/url_helper'
-require_relative 'helpers/assets_helper'
require_relative 'helpers/deltacloud_helper'
require_relative 'helpers/rabbit_helper'
require_relative 'helpers/blob_stream_helper'
@@ -36,7 +35,6 @@ module Deltacloud::Collections
helpers Deltacloud::Helpers::Drivers
helpers Sinatra::AuthHelper
helpers Sinatra::UrlForHelper
- helpers Sinatra::StaticAssets::Helpers
helpers Rack::RespondTo::Helpers
helpers Deltacloud::Helpers::Application
diff --git a/server/lib/deltacloud/helpers/application_helper.rb b/server/lib/deltacloud/helpers/application_helper.rb
deleted file mode 100644
index 7a0d58b..0000000
--- a/server/lib/deltacloud/helpers/application_helper.rb
+++ /dev/null
@@ -1,238 +0,0 @@
-# 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.
-
-# Methods added to this helper will be available to all templates in the application.
-
-require 'benchmark'
-
-module ApplicationHelper
-
- include Deltacloud
-
- def instance_action_method(action)
- action_method(action, :instances)
- end
-
- def action_method(action, collection)
- collections[collection].operations[action.to_sym].method
- end
-
- def driver_has_feature?(feature_name, collection_name = :instances)
- driver.features(collection_name).any? { |f| f.name == feature_name }
- end
-
- def driver_has_auth_features?
- driver_has_feature?(:authentication_password) || driver_has_feature?(:authentication_key)
- end
-
- def driver_auth_feature_name
- 'key' if driver_has_feature?(:authentication_key)
- 'password' if driver_has_feature?(:authentication_password)
- end
-
- def filter_all(model)
- filter = {}
- filter.merge!(:id => params[:id]) if params[:id]
- filter.merge!(:architecture => params[:architecture]) if params[:architecture]
- filter.merge!(:owner_id => params[:owner_id]) if params[:owner_id]
- filter.merge!(:state => params[:state]) if params[:state]
- filter = {} if filter.keys.size.eql?(0)
- singular = model.to_s.singularize.to_sym
- begin
- @benchmark = Benchmark.measure do
- @elements = driver.send(model.to_sym, credentials, filter)
- end
- rescue
- @exception = $!
- end
- if @elements
- headers['X-Backend-Runtime'] = @benchmark.real.to_s
- instance_variable_set(:"@#{model}", @elements)
- respond_to do |format|
- format.html { haml :"#{model}/index" }
- format.xml { haml :"#{model}/index" }
- format.json { convert_to_json(singular, @elements) }
- end
- else
- report_error(@exception.code)
- end
- end
-
- def show(model)
- @benchmark = Benchmark.measure do
- @element = driver.send(model, credentials, { :id => params[:id]} )
- end
- headers['X-Backend-Runtime'] = @benchmark.real.to_s
- instance_variable_set("@#{model}", @element)
- if @element
- respond_to do |format|
- format.html { haml :"#{model.to_s.pluralize}/show" }
- format.xml { haml :"#{model.to_s.pluralize}/show" }
- format.json { convert_to_json(model, @element) }
- end
- else
- report_error(404)
- end
- end
-
- def report_error(code=nil)
- @error, @code = (request.env['sinatra.error'] || @exception), code
- @code = 500 if not @code and not @error.class.method_defined? :code
- response.status = @code || @error.code
- respond_to do |format|
- format.xml { haml :"errors/#{@code || @error.code}", :layout => false }
- format.json { json_return_error(@error) }
- format.html { haml :"errors/#{@code || @error.code}", :layout => :error }
- end
- end
-
- def instance_action(name)
- original_instance = driver.instance(credentials, :id => params[:id])
-
- # If original instance doesn't include called action
- # return with 405 error (Method is not Allowed)
- unless driver.instance_actions_for(original_instance.state).include?(name.to_sym)
- return report_error(405)
- end
-
- @benchmark = Benchmark.measure do
- @instance = driver.send(:"#{name}_instance", credentials, params[:id])
- end
-
- headers['X-Backend-Runtime'] = @benchmark.real.to_s
-
- if name == :reboot
- status 202
- end
-
- if name == :destroy
- respond_to do |format|
- format.xml { return 204 }
- format.json { return 204 }
- format.html { return redirect(instances_url) }
- end
- end
-
- if @instance.class != Instance
- response['Location'] = instance_url(params[:id])
- halt
- end
-
- respond_to do |format|
- format.xml { haml :"instances/show" }
- format.html { haml :"instances/show" }
- format.json {convert_to_json(:instance, @instance) }
- end
- end
-
- def cdata(text = nil, &block)
- text ||= capture_haml(&block)
- "<![CDATA[#{text.strip}]]>"
- end
-
- def render_cdata(text)
- "<![CDATA[#{text.strip}]]>"
- end
-
- def link_to_action(action, url, method)
- capture_haml do
- haml_tag :form, :method => :post, :action => url, :class => [:link, method], :'data-ajax' => 'false' do
- haml_tag :input, :type => :hidden, :name => '_method', :value => method
- haml_tag :button, :type => :submit, :'data-ajax' => 'false', :'data-inline' => "true" do
- haml_concat action
- end
- end
- end
- end
-
- def link_to_format(format)
- return unless request.env['REQUEST_URI']
- uri = request.env['REQUEST_URI']
- return if uri.include?('format=')
- uri += uri.include?('?') ? "&format=#{format}" : "?format=#{format}"
- capture_haml do
- haml_tag :a, :href => uri, :'data-ajax' => 'false', :'data-icon' => 'grid' do
- haml_concat format.to_s.upcase
- end
- end
- end
-
- def image_for_state(state)
- state_img = "stopped" if (state!='RUNNING' or state!='PENDING')
- capture_haml do
- haml_tag :img, :src => "/images/#{state}" % state.downcase, :title => state
- end
- end
-
- # Reverse the entrypoints hash for a driver from drivers.yaml; note that
- # +d+ is a hash, not an actual driver object
- def driver_provider(d)
- result = {}
- if d[:entrypoints]
- d[:entrypoints].each do |kind, details|
- details.each do |prov, url|
- result[prov] ||= {}
- result[prov][kind] = url
- end
- end
- end
- result
- end
-
- def header(title, opts={}, &block)
- opts[:theme] ||= 'b'
- opts[:back] ||= 'true'
- capture_haml do
- haml_tag :div, :'data-role' => :header, :'data-theme' => opts[:theme], :'data-add-back-btn' => opts[:back] do
- haml_tag :a, :'data-rel' => :back do
- haml_concat "Back"
- end if opts[:back] == 'true'
- haml_tag :h1 do
- haml_concat title
- end
- block.call if block_given?
- end
- end
- end
-
- def subheader(title, opts={})
- opts[:theme] ||= 'a'
- capture_haml do
- haml_tag :div, :'data-role' => :header, :'data-theme' => opts[:theme] do
- haml_tag :p, :class => 'inner-right' do
- haml_concat title
- end
- end
- end
- end
-
- def translate_error_code(code)
- case code
- when 400; { :message => "Bad Request" }
- when 401; { :message => "Unauthorized" }
- when 403; { :message => "Forbidden" }
- when 404; { :message => "Not Found" }
- when 405; { :message => "Method Not Allowed" }
- when 406; { :message => "Not Acceptable" }
- when 500; { :message => "Internal Server Error" }
- when 502; { :message => "Backend Server Error" }
- when 501; { :message => "Not Supported" }
- end
- end
-
- def new_blob_form_url(bucket)
- bucket_url(@bucket.name) + "/" + NEW_BLOB_FORM_ID
- end
-end
diff --git a/server/lib/deltacloud/helpers/auth_helper.rb b/server/lib/deltacloud/helpers/auth_helper.rb
new file mode 100644
index 0000000..eef2521
--- /dev/null
+++ b/server/lib/deltacloud/helpers/auth_helper.rb
@@ -0,0 +1,73 @@
+#
+# 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.
+
+# Lazy Basic HTTP authentication. Authentication is only forced when the
+# credentials are actually needed.
+
+module Sinatra
+ module AuthHelper
+ class LazyCredentials
+ def initialize(app)
+ @app = app
+ @provided = false
+ end
+
+ def user
+ credentials!
+ @user
+ end
+
+ def password
+ credentials!
+ @password
+ end
+
+ def provided?
+ @provided
+ end
+
+ private
+ def credentials!
+ if ENV["API_USER"] && ENV["API_PASSWORD"]
+ @user = ENV["API_USER"]
+ @password = ENV["API_PASSWORD"]
+ @provided = true
+ end
+ unless provided?
+ auth = Rack::Auth::Basic::Request.new(@app.request.env)
+ @app.authorize! unless auth.provided? && auth.basic? && auth.credentials
+ @user = auth.credentials[0]
+ @password = auth.credentials[1]
+ @provided = true
+ end
+ end
+
+ end
+
+ def authorize!
+ r = "#{Thread.current[:driver]}-deltacloud@#{ENV['HOSTNAME']}"
+ response['WWW-Authenticate'] = %(Basic realm="#{r}")
+ throw(:halt, [401, report_error(401)])
+ end
+
+ # Request the current user's credentials. Actual credentials are only
+ # requested when an attempt is made to get the user name or password
+ def credentials
+ LazyCredentials.new(self)
+ end
+ end
+
+end
diff --git a/server/lib/deltacloud/helpers/blob_stream.rb b/server/lib/deltacloud/helpers/blob_stream.rb
deleted file mode 100644
index df5cdc6..0000000
--- a/server/lib/deltacloud/helpers/blob_stream.rb
+++ /dev/null
@@ -1,213 +0,0 @@
-# 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.
-
-include Deltacloud
-begin
- require 'eventmachine'
- #--
- # based on the example from
- # http://macournoyer.com/blog/2009/06/04/pusher-and-async-with-thin/
- #--
- class BlobStream
- AsyncResponse = [-1, {}, []].freeze
- def self.call(env, credentials, params)
- body = DeferrableBody.new
- #Get the headers out asap. Don't specify a content-type let
- #the client guess and if they can't they SHOULD default to
- #'application/octet-stream' anyway as per:
- #http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7.2.1
- EM.next_tick { env['async.callback'].call [200, {
- 'Content-Type' => "#{params['content_type']}",
- 'Content-Disposition' => params["content_disposition"],
- 'Content-Length' => "#{params['content_length']}"}, body]
- }
- #call the driver from here. the driver method yields for every chunk
- #of blob it receives. Then use body.call to write that chunk as received.
- driver.blob_data(credentials, params[:bucket], params[:blob], params) {|chunk| body.call ["#{chunk}"]} #close blob_data block
- body.succeed
- AsyncResponse # Tell Thin to not close connection & work other requests
- end
- end
-
- class DeferrableBody
- include EventMachine::Deferrable
-
- def call(body)
- body.each do |chunk|
- @body_callback.call(chunk)
- end
- end
-
- def each(&blk)
- @body_callback = blk
- end
- end
-rescue LoadError => e
- # EventMachine isn't available, disable blob streaming
- class BlobStream
- def self.call(env, credentials, params)
- raise NotImplementedError.new("Blob streaming is only supported under Thin")
- end
- end
-end
-
-module BlobHelper
-
- def self.extract_blob_metadata_hash(env_hash)
- meta_array = env_hash.select{|k,v| k.match(/^HTTP[-_]X[-_]Deltacloud[-_]Blobmeta[-_]/i)}
- metadata = meta_array.inject({}){ |result, array| result[array.first.upcase] = array.last; result}
- metadata
- end
-
-DELTACLOUD_BLOBMETA_HEADER = /HTTP[-_]X[-_]Deltacloud[-_]Blobmeta[-_]/i
-
- #e.g. from HTTP-X-Deltacloud-Blobmeta-FOO:BAR to amz-meta-FOO:BAR
- def self.rename_metadata_headers(metadata, rename_to)
- metadata.gsub_keys(DELTACLOUD_BLOBMETA_HEADER, rename_to)
- end
-
-end
-
-#Monkey patch for streaming blobs:
-# Normally a client will upload a blob to deltacloud and thin will put
-# this into a tempfile. Then deltacloud would stream up to the provider:
-# i.e. client =-->>TEMP_FILE-->> deltacloud =-->>STREAM-->> provider
-# Instead we want to recognise that this is a 'PUT blob' operation and
-# start streaming to the provider as the request is received:
-# i.e. client =-->>STREAM-->> deltacloud =-->>STREAM-->> provider
-module Thin
- class Request
-
- alias_method :move_body_to_tempfile_orig, :move_body_to_tempfile if defined?(Thin::Response)
- private
- def move_body_to_tempfile
- if BlobStreamIO::is_put_blob(self)
- @body = BlobStreamIO.new(self)
- else
- move_body_to_tempfile_orig
- end
- end
-
- end
-end
-
-require 'net/http'
-require 'net/https'
-#monkey patch for Net:HTTP
-module Net
- class HTTP
-
- alias :request_orig :request
-
- def request(req, body = nil, blob_stream = nil, &block)
- unless blob_stream
- return request_orig(req, body, &block)
- end
- @blob_req = req
- do_start #start the connection
-
- req.set_body_internal body
- begin_transport req
- req.write_header_m @socket,@curr_http_version, edit_path(req.path)
- @socket
- end
-
- class Put < HTTPRequest
- def write_header_m(sock, ver, path)
- write_header(sock, ver, path)
- end
- end
-
- def end_request
- begin
- res = HTTPResponse.read_new(@socket)
- end while res.kind_of?(HTTPContinue)
- res.reading_body(@socket, @blob_req.response_body_permitted?) {
- yield res if block_given? }
- end_transport @blob_req, res
- do_finish
- res
- end
- end
-
-end
-
-require 'base64'
-class BlobStreamIO
-
- attr_accessor :size, :provider, :sock
-
- def initialize(request)
- @client_request = request
- @size = 0
- bucket, blob = parse_bucket_blob(request.env["PATH_INFO"])
- user, password = parse_credentials(request.env['HTTP_AUTHORIZATION'])
- content_type = request.env['CONTENT_TYPE'] || ""
- #deal with blob_metadata: (X-Deltacloud-Blobmeta-name: value)
- user_meta = BlobHelper::extract_blob_metadata_hash(request.env)
- @content_length = request.env['CONTENT_LENGTH']
- @http, provider_request = driver.blob_stream_connection({:user=>user,
- :password=>password, :bucket=>bucket, :blob=>blob, :metadata=> user_meta,
- :content_type=>content_type, :content_length=>@content_length })
- @content_length = @content_length.to_i #for comparison of size in '<< (data)'
- @sock = @http.request(provider_request, nil, true)
- end
-
- def << (data)
- @sock.write(data)
- @size += data.length
- if (@size >= @content_length)
- result = @http.end_request
- if result.is_a?(Net::HTTPSuccess)
- @client_request.env["BLOB_SUCCESS"] = "true"
- else
- @client_request.env["BLOB_FAIL"] = result.body
- end
- end
- end
-
- def rewind
- end
-
- #use the Request.env hash (populated by the ThinParser) to determine whether
- #this is a post blob operation. By definition, only get here with a body of
- # > 112kbytes - thin/lib/thin/request.rb:12 MAX_BODY = 1024 * (80 + 32)
- def self.is_put_blob(request = nil)
- path = request.env['PATH_INFO']
- method = request.env['REQUEST_METHOD']
- if ( path =~ /^#{Regexp.escape(settings.root_url)}\/buckets/ && method == 'PUT' )
- return true
- else
- return false
- end
- end
-
- private
-
- def parse_bucket_blob(request_string)
- array = request_string.split("/")
- blob = array.pop
- bucket = array.pop
- return bucket, blob
- end
-
- def parse_credentials(request_string)
- decoded = Base64.decode64(request_string.split('Basic ').last)
- key = decoded.split(':').first
- pass = decoded.split(':').last
- return key, pass
- end
-
-end
diff --git a/server/lib/deltacloud/helpers/blob_stream_helper.rb b/server/lib/deltacloud/helpers/blob_stream_helper.rb
new file mode 100644
index 0000000..3830c60
--- /dev/null
+++ b/server/lib/deltacloud/helpers/blob_stream_helper.rb
@@ -0,0 +1,214 @@
+# 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.
+
+include Deltacloud
+
+begin
+ require 'eventmachine'
+ #--
+ # based on the example from
+ # http://macournoyer.com/blog/2009/06/04/pusher-and-async-with-thin/
+ #--
+ class BlobStream
+ AsyncResponse = [-1, {}, []].freeze
+ def self.call(env, credentials, params)
+ body = DeferrableBody.new
+ #Get the headers out asap. Don't specify a content-type let
+ #the client guess and if they can't they SHOULD default to
+ #'application/octet-stream' anyway as per:
+ #http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7.2.1
+ EM.next_tick { env['async.callback'].call [200, {
+ 'Content-Type' => "#{params['content_type']}",
+ 'Content-Disposition' => params["content_disposition"],
+ 'Content-Length' => "#{params['content_length']}"}, body]
+ }
+ #call the driver from here. the driver method yields for every chunk
+ #of blob it receives. Then use body.call to write that chunk as received.
+ driver.blob_data(credentials, params[:bucket], params[:blob], params) {|chunk| body.call ["#{chunk}"]} #close blob_data block
+ body.succeed
+ AsyncResponse # Tell Thin to not close connection & work other requests
+ end
+ end
+
+ class DeferrableBody
+ include EventMachine::Deferrable
+
+ def call(body)
+ body.each do |chunk|
+ @body_callback.call(chunk)
+ end
+ end
+
+ def each(&blk)
+ @body_callback = blk
+ end
+ end
+rescue LoadError => e
+ # EventMachine isn't available, disable blob streaming
+ class BlobStream
+ def self.call(env, credentials, params)
+ raise NotImplementedError.new("Blob streaming is only supported under Thin")
+ end
+ end
+end
+
+module BlobHelper
+
+ def self.extract_blob_metadata_hash(env_hash)
+ meta_array = env_hash.select{|k,v| k.match(/^HTTP[-_]X[-_]Deltacloud[-_]Blobmeta[-_]/i)}
+ metadata = meta_array.inject({}){ |result, array| result[array.first.upcase] = array.last; result}
+ metadata
+ end
+
+DELTACLOUD_BLOBMETA_HEADER = /HTTP[-_]X[-_]Deltacloud[-_]Blobmeta[-_]/i
+
+ #e.g. from HTTP-X-Deltacloud-Blobmeta-FOO:BAR to amz-meta-FOO:BAR
+ def self.rename_metadata_headers(metadata, rename_to)
+ metadata.gsub_keys(DELTACLOUD_BLOBMETA_HEADER, rename_to)
+ end
+
+end
+
+#Monkey patch for streaming blobs:
+# Normally a client will upload a blob to deltacloud and thin will put
+# this into a tempfile. Then deltacloud would stream up to the provider:
+# i.e. client =-->>TEMP_FILE-->> deltacloud =-->>STREAM-->> provider
+# Instead we want to recognise that this is a 'PUT blob' operation and
+# start streaming to the provider as the request is received:
+# i.e. client =-->>STREAM-->> deltacloud =-->>STREAM-->> provider
+module Thin
+ class Request
+
+ alias_method :move_body_to_tempfile_orig, :move_body_to_tempfile if defined?(Thin::Response)
+ private
+ def move_body_to_tempfile
+ if BlobStreamIO::is_put_blob(self)
+ @body = BlobStreamIO.new(self)
+ else
+ move_body_to_tempfile_orig
+ end
+ end
+
+ end
+end
+
+require 'net/http'
+require 'net/https'
+#monkey patch for Net:HTTP
+module Net
+ class HTTP
+
+ alias :request_orig :request
+
+ def request(req, body = nil, blob_stream = nil, &block)
+ unless blob_stream
+ return request_orig(req, body, &block)
+ end
+ @blob_req = req
+ do_start #start the connection
+
+ req.set_body_internal body
+ begin_transport req
+ req.write_header_m @socket,@curr_http_version, edit_path(req.path)
+ @socket
+ end
+
+ class Put < HTTPRequest
+ def write_header_m(sock, ver, path)
+ write_header(sock, ver, path)
+ end
+ end
+
+ def end_request
+ begin
+ res = HTTPResponse.read_new(@socket)
+ end while res.kind_of?(HTTPContinue)
+ res.reading_body(@socket, @blob_req.response_body_permitted?) {
+ yield res if block_given? }
+ end_transport @blob_req, res
+ do_finish
+ res
+ end
+ end
+
+end
+
+require 'base64'
+class BlobStreamIO
+
+ attr_accessor :size, :provider, :sock
+
+ def initialize(request)
+ @client_request = request
+ @size = 0
+ bucket, blob = parse_bucket_blob(request.env["PATH_INFO"])
+ user, password = parse_credentials(request.env['HTTP_AUTHORIZATION'])
+ content_type = request.env['CONTENT_TYPE'] || ""
+ #deal with blob_metadata: (X-Deltacloud-Blobmeta-name: value)
+ user_meta = BlobHelper::extract_blob_metadata_hash(request.env)
+ @content_length = request.env['CONTENT_LENGTH']
+ @http, provider_request = driver.blob_stream_connection({:user=>user,
+ :password=>password, :bucket=>bucket, :blob=>blob, :metadata=> user_meta,
+ :content_type=>content_type, :content_length=>@content_length })
+ @content_length = @content_length.to_i #for comparison of size in '<< (data)'
+ @sock = @http.request(provider_request, nil, true)
+ end
+
+ def << (data)
+ @sock.write(data)
+ @size += data.length
+ if (@size >= @content_length)
+ result = @http.end_request
+ if result.is_a?(Net::HTTPSuccess)
+ @client_request.env["BLOB_SUCCESS"] = "true"
+ else
+ @client_request.env["BLOB_FAIL"] = result.body
+ end
+ end
+ end
+
+ def rewind
+ end
+
+ #use the Request.env hash (populated by the ThinParser) to determine whether
+ #this is a post blob operation. By definition, only get here with a body of
+ # > 112kbytes - thin/lib/thin/request.rb:12 MAX_BODY = 1024 * (80 + 32)
+ def self.is_put_blob(request = nil)
+ path = request.env['PATH_INFO']
+ method = request.env['REQUEST_METHOD']
+ if ( path =~ /^#{Regexp.escape(Deltacloud::API.settings.root_url)}\/buckets/ && method == 'PUT' )
+ return true
+ else
+ return false
+ end
+ end
+
+ private
+
+ def parse_bucket_blob(request_string)
+ array = request_string.split("/")
+ blob = array.pop
+ bucket = array.pop
+ return bucket, blob
+ end
+
+ def parse_credentials(request_string)
+ decoded = Base64.decode64(request_string.split('Basic ').last)
+ key = decoded.split(':').first
+ pass = decoded.split(':').last
+ return key, pass
+ end
+
+end
diff --git a/server/lib/deltacloud/helpers/conversion_helper.rb b/server/lib/deltacloud/helpers/conversion_helper.rb
deleted file mode 100644
index 310ba4b..0000000
--- a/server/lib/deltacloud/helpers/conversion_helper.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-# 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.
-
-
-require 'deltacloud/base_driver'
-
-module ConversionHelper
-
- def convert_to_json(type, obj)
- if ( [ :image, :realm, :instance, :storage_volume, :storage_snapshot, :hardware_profile, :key, :bucket, :blob, :firewall, :load_balancer, :address ].include?( type ) )
- if Array.eql?(obj.class)
- data = obj.collect do |o|
- o.to_hash.merge({ :href => self.send(:"#{type}_url", type.eql?(:hardware_profile) ? o.name : o.id ) })
- end
- type = type.to_s.pluralize
- else
- data = obj.to_hash
- if type == :blob
- data.merge!({ :href => self.send(:"bucket_url", "#{data[:bucket]}/#{data[:id]}" ) })
- else
- data.merge!({ :href => self.send(:"#{type}_url", data[:id]) })
- if data.has_key?(:hardware_profiles)
- data[:hardware_profiles] = data[:hardware_profiles].inject([]){|res, hwp| res << {hwp.name => {:href => self.send(:"hardware_profile_url", hwp.name)}}; res }
- end
- end
- end
- return { :"#{type}" => data }.to_json
- end
- end
-
-end
diff --git a/server/lib/deltacloud/helpers/deltacloud_helper.rb b/server/lib/deltacloud/helpers/deltacloud_helper.rb
new file mode 100644
index 0000000..83741f9
--- /dev/null
+++ b/server/lib/deltacloud/helpers/deltacloud_helper.rb
@@ -0,0 +1,273 @@
+# 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::Helpers
+ module Application
+
+ require 'benchmark'
+
+ def self.included(klass)
+ klass.class_eval do
+ set :root_url, API_ROOT_URL
+ include Sinatra::Rabbit
+ Sinatra::Rabbit.set :root_path, root_url+'/'
+ end
+ end
+
+ def instance_action_method(action)
+ action_method(action, Sinatra::Rabbit::InstancesCollection)
+ end
+
+ def action_method(action, collection)
+ http_method = collection.operation(action).http_method
+ http_method || Sinatra::Rabbit::BaseCollection.http_method_for(action)
+ end
+
+ def filter_all(model)
+ filter = {}
+ filter.merge!(:id => params[:id]) if params[:id]
+ filter.merge!(:architecture => params[:architecture]) if params[:architecture]
+ filter.merge!(:owner_id => params[:owner_id]) if params[:owner_id]
+ filter.merge!(:state => params[:state]) if params[:state]
+ filter = {} if filter.keys.size.eql?(0)
+ singular = model.to_s.singularize.to_sym
+ begin
+ @benchmark = Benchmark.measure do
+ @elements = driver.send(model.to_sym, credentials, filter)
+ end
+ rescue
+ @exception = $!
+ end
+ if @elements
+ headers['X-Backend-Runtime'] = @benchmark.real.to_s
+ instance_variable_set(:"@#{model}", @elements)
+ respond_to do |format|
+ format.html { haml :"#{model}/index" }
+ format.xml { haml :"#{model}/index" }
+ format.json { @media_type=:xml; to_json(haml(:"#{model}/index")) }
+ end
+ else
+ report_error(@exception.respond_to?(:code) ? @exception.code : 500)
+ end
+ end
+
+ def xml_to_json(model)
+ @media_type = :xml
+ to_json(haml(:"#{model}"))
+ end
+
+ def to_json(xml)
+ Crack::XML.parse(xml).to_json
+ end
+
+ def show(model)
+ @benchmark = Benchmark.measure do
+ @element = driver.send(model, credentials, { :id => params[:id]} )
+ end
+ headers['X-Backend-Runtime'] = @benchmark.real.to_s
+ instance_variable_set("@#{model}", @element)
+ if @element
+ respond_to do |format|
+ format.html { haml :"#{model.to_s.pluralize}/show" }
+ format.xml { haml :"#{model.to_s.pluralize}/show" }
+ format.json { @media_type=:xml; to_json(haml(:"#{model.to_s.pluralize}/show")) }
+ end
+ else
+ report_error(404)
+ end
+ end
+
+ def report_error(code=nil)
+ @error, @code = (request.env['sinatra.error'] || @exception), code
+ @code = 500 if not @code and not @error.class.method_defined? :code
+ response.status = @code || @error.code
+ respond_to do |format|
+ format.xml { haml :"errors/#{@code || @error.code}", :layout => false }
+ format.html { haml :"errors/#{@code || @error.code}", :layout => :error }
+ end
+ end
+
+ def instance_action(name)
+ original_instance = driver.instance(credentials, :id => params[:id])
+
+ # If original instance doesn't include called action
+ # return with 405 error (Method is not Allowed)
+ unless driver.instance_actions_for(original_instance.state).include?(name.to_sym)
+ return report_error(405)
+ end
+
+ @benchmark = Benchmark.measure do
+ @instance = driver.send(:"#{name}_instance", credentials, params[:id])
+ end
+
+ headers['X-Backend-Runtime'] = @benchmark.real.to_s
+ status 202
+
+ if name == :destroy
+ respond_to do |format|
+ format.xml { return 204 }
+ format.json { return 204 }
+ format.html { return redirect(instances_url) }
+ end
+ end
+
+ if @instance.class != Instance
+ response['Location'] = instance_url(params[:id])
+ halt
+ end
+
+ respond_to do |format|
+ format.xml { haml :"instances/show" }
+ format.html { haml :"instances/show" }
+ format.json {convert_to_json(:instance, @instance) }
+ end
+ end
+
+ def cdata(text = nil, &block)
+ text ||= capture_haml(&block)
+ "<![CDATA[#{text.strip}]]>"
+ end
+
+ def render_cdata(text)
+ "<![CDATA[#{text.strip}]]>"
+ end
+
+ def link_to_action(action, url, method)
+ capture_haml do
+ haml_tag :form, :method => :post, :action => url, :class => [:link, method], :'data-ajax' => 'false' do
+ haml_tag :input, :type => :hidden, :name => '_method', :value => method
+ haml_tag :button, :type => :submit, :'data-ajax' => 'false', :'data-inline' => "true" do
+ haml_concat action
+ end
+ end
+ end
+ end
+
+ def link_to_format(format)
+ return unless request.env['REQUEST_URI']
+ uri = request.env['REQUEST_URI']
+ return if uri.include?('format=')
+ uri += uri.include?('?') ? "&format=#{format}" : "?format=#{format}"
+ capture_haml do
+ haml_tag :a, :href => uri, :'data-ajax' => 'false', :'data-icon' => 'grid' do
+ haml_concat format.to_s.upcase
+ end
+ end
+ end
+
+ def image_for_state(state)
+ state_img = "stopped" if (state!='RUNNING' or state!='PENDING')
+ capture_haml do
+ haml_tag :img, :src => "/images/#{state}" % state.downcase, :title => state
+ end
+ end
+
+ # Reverse the entrypoints hash for a driver from drivers.yaml; note that
+ # +d+ is a hash, not an actual driver object
+ def driver_provider(d)
+ result = {}
+ if d[:entrypoints]
+ d[:entrypoints].each do |kind, details|
+ details.each do |prov, url|
+ result[prov] ||= {}
+ result[prov][kind] = url
+ end
+ end
+ end
+ result
+ end
+
+ def header(title, opts={}, &block)
+ opts[:theme] ||= 'b'
+ opts[:back] ||= 'true'
+ capture_haml do
+ haml_tag :div, :'data-role' => :header, :'data-theme' => opts[:theme], :'data-add-back-btn' => opts[:back] do
+ haml_tag :a, :'data-rel' => :back do
+ haml_concat "Back"
+ end if opts[:back] == 'true'
+ haml_tag :h1 do
+ haml_concat title
+ end
+ block.call if block_given?
+ end
+ end
+ end
+
+ def subheader(title, opts={})
+ opts[:theme] ||= 'a'
+ capture_haml do
+ haml_tag :div, :'data-role' => :header, :'data-theme' => opts[:theme] do
+ haml_tag :p, :class => 'inner-right' do
+ haml_concat title
+ end
+ end
+ end
+ end
+
+ def translate_error_code(code)
+ case code
+ when 400; { :message => "Bad Request" }
+ when 401; { :message => "Unauthorized" }
+ when 403; { :message => "Forbidden" }
+ when 404; { :message => "Not Found" }
+ when 405; { :message => "Method Not Allowed" }
+ when 406; { :message => "Not Acceptable" }
+ when 500; { :message => "Internal Server Error" }
+ when 502; { :message => "Backend Server Error" }
+ when 501; { :message => "Not Supported" }
+ end
+ end
+
+ def new_blob_form_url(bucket)
+ bucket_url(@bucket.name) + "/" + NEW_BLOB_FORM_ID
+ end
+
+ def format_hardware_property(prop)
+ return "∅" unless prop
+ u = hardware_property_unit(prop)
+ case prop.kind
+ when :range
+ "#{prop.first} #{u} - #{prop.last} #{u} (default: #{prop.default} #{u})"
+ when :enum
+ prop.values.collect{ |v| "#{v} #{u}"}.join(', ') + " (default: #{prop.default} #{u})"
+ else
+ "#{prop.value} #{u}"
+ end
+ end
+
+ def format_instance_profile(ip)
+ o = ip.overrides.collect do |p, v|
+ u = hardware_property_unit(p)
+ "#{p} = #{v} #{u}"
+ end
+ if o.empty?
+ ""
+ else
+ "with #{o.join(", ")}"
+ end
+ end
+
+ private
+ def hardware_property_unit(prop)
+ u = ::Deltacloud::HardwareProfile::unit(prop)
+ u = "" if ["label", "count"].include?(u)
+ u = "vcpus" if prop == :cpu
+ u
+ end
+
+
+
+ end
+end
diff --git a/server/lib/deltacloud/helpers/driver_helper.rb b/server/lib/deltacloud/helpers/driver_helper.rb
new file mode 100644
index 0000000..f620744
--- /dev/null
+++ b/server/lib/deltacloud/helpers/driver_helper.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::Helpers
+
+ module Drivers
+
+ def driver_symbol
+ driver_name.to_sym
+ end
+
+ def driver_name
+ Thread.current[:driver] ||= ENV['API_DRIVER']
+ 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
+ begin
+ m = Deltacloud::Drivers.const_get(driver_class_name)
+ m.const_get(driver_class_name + "Driver").new
+ rescue NameError
+ raise "[ERROR] The driver class name is not defined as #{driver_class_name}Driver"
+ end
+ end
+
+ def driver
+ $:.unshift File.join(File.dirname(__FILE__), '..', '..')
+ begin
+ require_relative driver_source_name
+ driver_class
+ rescue LoadError
+ raise "[ERROR] The driver '#{driver_name}' is unknown or not installed (#{driver_source_name})"
+ end
+ end
+
+ end
+
+end
diff --git a/server/lib/deltacloud/helpers/hardware_profiles_helper.rb b/server/lib/deltacloud/helpers/hardware_profiles_helper.rb
deleted file mode 100644
index af2100e..0000000
--- a/server/lib/deltacloud/helpers/hardware_profiles_helper.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-# 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 HardwareProfilesHelper
-
- def format_hardware_property(prop)
- return "∅" unless prop
- u = hardware_property_unit(prop)
- case prop.kind
- when :range
- "#{prop.first} #{u} - #{prop.last} #{u} (default: #{prop.default} #{u})"
- when :enum
- prop.values.collect{ |v| "#{v} #{u}"}.join(', ') + " (default: #{prop.default} #{u})"
- else
- "#{prop.value} #{u}"
- end
- end
-
- def format_instance_profile(ip)
- o = ip.overrides.collect do |p, v|
- u = hardware_property_unit(p)
- "#{p} = #{v} #{u}"
- end
- if o.empty?
- nil
- else
- "with #{o.join(", ")}"
- end
- end
-
- private
- def hardware_property_unit(prop)
- u = ::Deltacloud::HardwareProfile::unit(prop)
- u = "" if ["label", "count"].include?(u)
- u = "vcpus" if prop == :cpu
- u
- end
-end
diff --git a/server/lib/deltacloud/helpers/json_helper.rb b/server/lib/deltacloud/helpers/json_helper.rb
deleted file mode 100644
index aea16b6..0000000
--- a/server/lib/deltacloud/helpers/json_helper.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-# 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 JSONHelper
-
- def json_features_for_entrypoint(entrypoint)
- features = driver.features(entrypoint.first).collect { |feature| feature.name }
- features.empty? ? {} : { :features => features }
- end
-
- def json_return_error(error)
- error_output=Hash.new
- error_output[:url] =request.env['REQUEST_URI']
- error_output[:status] =response.status
- error_output[:message]=error.message if error
- error_output.to_json
- end
-
-end
diff --git a/server/lib/deltacloud/helpers/rabbit_helper.rb b/server/lib/deltacloud/helpers/rabbit_helper.rb
new file mode 100644
index 0000000..252abe2
--- /dev/null
+++ b/server/lib/deltacloud/helpers/rabbit_helper.rb
@@ -0,0 +1,34 @@
+# 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.
+
+
+Sinatra::Rabbit::Collection.class_eval do
+
+ def self.standard_index_operation
+ collection_name = @collection_name
+ operation :index, :with_capability => collection_name do
+ control { filter_all collection_name }
+ end
+ end
+
+ def self.standard_show_operation
+ collection_name = @collection_name
+ operation :show, :with_capability => collection_name do
+ control { show collection_name.to_s.singularize.intern }
+ end
+ end
+
+end
+
diff --git a/server/lib/deltacloud/helpers/url_helper.rb b/server/lib/deltacloud/helpers/url_helper.rb
new file mode 100644
index 0000000..87bc93e
--- /dev/null
+++ b/server/lib/deltacloud/helpers/url_helper.rb
@@ -0,0 +1,112 @@
+#
+# Based on https://github.com/emk/sinatra-url-for/
+# Commit 1df339284203f8f6ed8d
+#
+# Original license:
+# Copyright (C) 2009 Eric Kidd
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to permit
+# persons to whom the Software is furnished to do so, subject to the
+# following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+# NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+# USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+module Sinatra
+ module UrlForHelper
+
+ require 'uri'
+
+ def method_missing(name, *args)
+ if name.to_s =~ /^([\w\_]+)_url$/
+ if args.size > 0
+ t = $1
+ if t =~ /^(stop|reboot|destroy|start|attach|detach)_/
+ api_url_for(t.pluralize.split('_').last + '/' + args.first + '/' + $1, :full)
+ else
+ api_url_for(t.pluralize, :full) + '/' + "#{args.first}"
+ end
+ else
+ api_url_for($1, :full)
+ end
+ else
+ super
+ end
+ end
+
+ def api_url_for(url_fragment, mode=:path_only)
+ matrix_params = ''
+ if request.params['api']
+ matrix_params += ";provider=%s" % request.params['api']['provider'] if request.params['api']['provider']
+ matrix_params += ";driver=%s" % request.params['api']['driver'] if request.params['api']['driver']
+ end
+ url_fragment = "/#{url_fragment}" unless url_fragment =~ /^\// # There is no need to prefix URI with '/'
+ if mode == :path_only
+ url_for "#{settings.root_url}#{matrix_params}#{url_fragment}", mode
+ else
+ url_for "#{matrix_params}#{url_fragment}", :full
+ end
+ end
+
+ # Construct a link to +url_fragment+, which should be given relative to
+ # the base of this Sinatra app. The mode should be either
+ # <code>:path_only</code>, which will generate an absolute path within
+ # the current domain (the default), or <code>:full</code>, which will
+ # include the site name and port number. (The latter is typically
+ # necessary for links in RSS feeds.) Example usage:
+ #
+ # url_for "/" # Returns "/myapp/"
+ # url_for "/foo" # Returns "/myapp/foo"
+ # url_for "/foo", :full # Returns "http://example.com/myapp/foo"
+ #--
+ # See README.rdoc for a list of some of the people who helped me clean
+ # up earlier versions of this code.
+ def url_for url_fragment, mode=:path_only
+ case mode
+ when :path_only
+ base = request.script_name
+ when :full
+ scheme = request.scheme
+ port = request.port
+ request_host = request.host
+ if request.env['HTTP_X_FORWARDED_FOR']
+ scheme = request.env['HTTP_X_FORWARDED_SCHEME'] || scheme
+ port = request.env['HTTP_X_FORWARDED_PORT']
+ request_host = request.env['HTTP_X_FORWARDED_HOST']
+ end
+ if (port.nil? || port == "" ||
+ (scheme == 'http' && port.to_s == '80') ||
+ (scheme == 'https' && port.to_s == '443'))
+ port = ""
+ else
+ port = ":#{port}"
+ end
+ base = "#{scheme}://#{request_host}#{port}#{request.script_name}"
+ else
+ raise TypeError, "Unknown url_for mode #{mode}"
+ end
+ uri_parser = URI.const_defined?(:Parser) ? URI::Parser.new : URI
+ url_escape = uri_parser.escape(url_fragment)
+ # Don't add the base fragment if url_for gets called more than once
+ # per url or the url_fragment passed in is an absolute url
+ if url_escape.match(/^#{base}/) or url_escape.match(/^http/)
+ url_escape
+ else
+ "#{base}#{url_escape}"
+ end
+ end
+ end
+
+end
diff --git a/server/lib/deltacloud/method_serializer.rb b/server/lib/deltacloud/method_serializer.rb
deleted file mode 100644
index eec9abe..0000000
--- a/server/lib/deltacloud/method_serializer.rb
+++ /dev/null
@@ -1,83 +0,0 @@
-#
-# 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.
-
-require 'base64'
-require 'digest'
-
-module MethodSerializer
-
- module Cache
-
- def cache_dir
- storage_dir = $methods_cache_dir || File.join(File.dirname(__FILE__), 'cache')
- class_dir = self.class.name.split('::').last
- class_dir ||= self.class.name
- File.join(storage_dir, class_dir.downcase)
- end
-
- def serialize_data(method_name, args, data)
- File.open(cache_file_name(method_name, args), 'w') do |f|
- f.puts(Base64.encode64(Marshal.dump(data)))
- end
- return data
- end
-
- def deserialize_data(method_name, args)
- begin
- data = File.readlines(cache_file_name(method_name, args)).join
- Marshal.load(Base64.decode64(data))
- rescue Errno::ENOENT
- return false
- end
- end
-
- def args_hash(args)
- if args.class == Hash
- args = args.to_a.collect {|i| [i[0].to_s, i[1]]}.sort
- end
- Digest::SHA1.hexdigest(args.to_s)
- end
-
- def cache_file_name(method_name, args)
- FileUtils.mkdir_p(cache_dir) unless File.directory?(cache_dir)
- method_name = $scenario_prefix ? "#{$scenario_prefix}_#{method_name}" : method_name
- File.join(cache_dir, "#{method_name}.#{args_hash(args)}")
- end
-
- def self.wrap_methods(c, opts={})
- $methods_cache_dir = opts[:cache_dir]
- $scenario_prefix = nil
- c.class_eval do
- cached_methods.each do |m|
- next if c.instance_methods(false).include?("original_#{m}")
- alias_method "original_#{m}".to_sym, m.to_sym
- define_method m.to_sym do |*args|
- args = args.first if args.size.eql?(1) and not args.first.class.eql?(Array)
- output = deserialize_data(m, args)
- unless output
- output = method("original_#{m}".to_sym).to_proc[args]
- return serialize_data(m, args, output)
- else
- return output
- end
- end
- end
- end
- end
-
- end
-
-end
diff --git a/server/lib/deltacloud/models/instance.rb b/server/lib/deltacloud/models/instance.rb
index c6835a9..c2cc0e3 100644
--- a/server/lib/deltacloud/models/instance.rb
+++ b/server/lib/deltacloud/models/instance.rb
@@ -16,8 +16,6 @@
class Instance < BaseModel
- include ApplicationHelper
-
attr_accessor :owner_id
attr_accessor :image_id
attr_accessor :name
diff --git a/server/lib/deltacloud/models/key.rb b/server/lib/deltacloud/models/key.rb
index 86a6283..64c4cc7 100644
--- a/server/lib/deltacloud/models/key.rb
+++ b/server/lib/deltacloud/models/key.rb
@@ -23,6 +23,10 @@ class Key < BaseModel
attr_accessor :pem_rsa_key
attr_accessor :state
+ def name
+ @name || @id
+ end
+
def is_password?
true if @credential_type.eql?(:password)
end
diff --git a/server/lib/deltacloud/validation.rb b/server/lib/deltacloud/validation.rb
deleted file mode 100644
index 3d29225..0000000
--- a/server/lib/deltacloud/validation.rb
+++ /dev/null
@@ -1,100 +0,0 @@
-#
-# 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::Validation
-
- class Param
- attr_reader :name, :klass, :type, :options, :description
-
- def initialize(args)
- @name = args[0]
- @klass = args[1] || :string
- @type = args[2] || :optional
- @options = args[3] || []
- @description = args[4] || ''
- end
-
- def required?
- type.eql?(:required)
- end
-
- def optional?
- type.eql?(:optional)
- end
-
- def valid_value?(value)
- true if (options.kind_of?(Range) or options.kind_of?(Array)) and options.include?(value)
- true if options.kind_of?(String) and not options.empty?
- end
-
- def valid_hwp_value?(profile, value)
- profile.property(@name.to_s.gsub(/^hwp_/, '')).valid?(value)
- end
-
- def hwp_property?
- true if name.to_s =~ /^hwp_(cpu|memory|storage|architecture)/
- end
- end
-
- def param(*args)
- raise "Duplicate param #{args[0]} #{params.inspect} #{self.class.name}" if params[args[0]]
- p = Param.new(args)
- params[p.name] = p
- end
-
- def params
- @params ||= {}
- @params
- end
-
- # Add the parameters in hash +new+ to already existing parameters. If
- # +new+ contains a parameter with an already existing name, the old
- # definition is clobbered.
- def add_params(new)
- # We do not check for duplication on purpose: multiple calls
- # to add_params should be cumulative
- new.each { |p| @params[p.name] = p }
- end
-
- def each_param(&block)
- params.each_value { |p| yield p }
- end
-
- def validate(current_driver, all_params, values, credentials)
- all_params.each do |key, p|
- if p.required? and not values[p.name]
- raise validation_exception "Required parameter #{p.name} not found"
- end
- next unless values[p.name]
- if p.hwp_property?
- profile = current_driver.hardware_profile(credentials, values['hwp_id'])
- raise validation_exception("Unknown hardware profile selected #{values['hwp_id']}") unless profile
- unless p.valid_hwp_value?(profile, values[p.name])
- raise validation_exception("Hardware profile property #{p.name} has invalid value #{values[p.name]}")
- end
- else
- if not p.options.empty? and p.valid_value?(values[p.name])
- raise validation_exception("Parameter #{p.name} has value #{values[p.name]} which is not in #{p.options.join(", ")}")
- end
- end
- end
- end
-
- def validation_exception(message)
- Deltacloud::ExceptionHandler::ValidationFailure.new(StandardError.new(message))
- end
-
-end
diff --git a/server/lib/sinatra/lazy_auth.rb b/server/lib/sinatra/lazy_auth.rb
deleted file mode 100644
index fb94dd9..0000000
--- a/server/lib/sinatra/lazy_auth.rb
+++ /dev/null
@@ -1,75 +0,0 @@
-#
-# 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.
-
-require 'sinatra/base'
-
-# Lazy Basic HTTP authentication. Authentication is only forced when the
-# credentials are actually needed.
-module Sinatra
- module LazyAuth
- class LazyCredentials
- def initialize(app)
- @app = app
- @provided = false
- end
-
- def user
- credentials!
- @user
- end
-
- def password
- credentials!
- @password
- end
-
- def provided?
- @provided
- end
-
- private
- def credentials!
- if ENV["API_USER"] && ENV["API_PASSWORD"]
- @user = ENV["API_USER"]
- @password = ENV["API_PASSWORD"]
- @provided = true
- end
- unless provided?
- auth = Rack::Auth::Basic::Request.new(@app.request.env)
- @app.authorize! unless auth.provided? && auth.basic? && auth.credentials
- @user = auth.credentials[0]
- @password = auth.credentials[1]
- @provided = true
- end
- end
-
- end
-
- def authorize!
- r = "#{driver_symbol}-deltacloud@#{HOSTNAME}"
- response['WWW-Authenticate'] = %(Basic realm="#{r}")
- throw(:halt, [401, report_error(401)])
- end
-
- # Request the current user's credentials. Actual credentials are only
- # requested when an attempt is made to get the user name or password
- def credentials
- LazyCredentials.new(self)
- end
- end
-
- helpers LazyAuth
-end
diff --git a/server/lib/sinatra/rabbit.rb b/server/lib/sinatra/rabbit.rb
deleted file mode 100644
index 5c63fd9..0000000
--- a/server/lib/sinatra/rabbit.rb
+++ /dev/null
@@ -1,441 +0,0 @@
-#
-# 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.
-
-require 'sinatra/base'
-require 'sinatra/url_for'
-require 'deltacloud/validation'
-require 'deltacloud/backend_capability'
-
-module Sinatra
-
- module Rabbit
-
- def self.routes
- @routes ||= []
- end
-
- class DuplicateParamException < Deltacloud::ExceptionHandler::DeltacloudException; end
- class DuplicateOperationException < Deltacloud::ExceptionHandler::DeltacloudException; end
- class DuplicateCollectionException < Deltacloud::ExceptionHandler::DeltacloudException; end
- class UnsupportedCollectionException < Deltacloud::ExceptionHandler::DeltacloudException
- def initialize
- # The server understood the request, but is refusing to fulfill it. Authorization will not help and the request
- # SHOULD NOT be repeated. If the request method was not HEAD and the server wishes to make public why the request
- # has not been fulfilled, it SHOULD describe the reason for the refusal in the entity. If the server does not wish
- # to make this information available to the client, the status code 404 (Not Found) can be used instead.
- super(403, 'UnsupportedCollection', "Requested collection is not supported for current provider", [])
- end
- end
-
- class Operation
- attr_reader :name, :method, :collection, :member
-
- include ::Deltacloud::BackendCapability
- include ::Deltacloud::Validation
- include ::ApplicationHelper
-
- STANDARD = {
- :new => { :method => :get, :member => false, :form => true },
- :index => { :method => :get, :member => false },
- :show => { :method => :get, :member => true },
- :create => { :method => :post, :member => false },
- :update => { :method => :put, :member => true },
- :destroy => { :method => :delete, :member => true }
- }
-
- def initialize(coll, name, opts, &block)
- @name = name.to_sym
- opts = STANDARD[@name].merge(opts) if standard?
- @path_generator = opts[:path_generator]
- @collection, @standard = coll, opts[:standard]
- raise "No method for operation #{name}" unless opts[:method]
- @method = opts[:method].to_sym
- @member = opts[:member]
- @description = ""
- instance_eval(&block) if block_given?
- generate_documentation
- generate_options
- end
-
- def http_method
- @method
- end
-
- def standard?
- STANDARD.keys.include?(name) || @standard
- end
-
- def form?
- STANDARD[name] and STANDARD[name][:form]
- end
-
- def description(text="")
- return @description if text.blank?
- @description = text
- end
-
- def generate_documentation
- coll, oper = @collection, self
- Rabbit::routes << [:get, "#{settings.root_url}/docs/#{@collection.name}/#{@name}"]
- ::Sinatra::Application.get("#{settings.root_url}/docs/#{@collection.name}/#{@name}") do
- @collection, @operation = coll, oper
- @features = driver.features_for_operation(coll.name, oper.name)
- respond_to do |format|
- format.html { haml :'docs/operation' }
- format.xml { haml :'docs/operation' }
- end
- end
- end
-
- def generate_options
- current_operation = self
- Rabbit::routes << [:options, "#{settings.root_url}/#{current_operation.collection.name}/#{current_operation.name}"]
- ::Sinatra::Application.options("#{settings.root_url}/#{current_operation.collection.name}/#{current_operation.name}") do
- required_params = current_operation.effective_params(driver).collect do |name, validation|
- name.to_s if validation.type.eql?(:required)
- end.compact.join(',')
- optional_params = current_operation.effective_params(driver).collect do |name, validation|
- name.to_s if validation.type.eql?(:optional)
- end.compact.join(',')
- headers 'X-Required-Parameters' => required_params
- headers 'X-Optional-Parameters' => optional_params
- [200, '']
- end
- end
-
- def control(&block)
- op = self
- @control = Proc.new do
- op.collection.check_supported(driver)
- op.check_capability(driver)
- op.validate(driver, op.effective_params(driver), params, credentials)
- instance_eval(&block)
- end
- end
-
- def member?
- if standard?
- @member || STANDARD[name][:member]
- else
- @member
- end
- end
-
- def path(args = {})
- return @path_generator.call(self) if @path_generator
- if member?
- if standard?
- "#{@collection.name}/:id"
- else
- "#{@collection.name}/:id/#{name}"
- end
- else
- if form?
- "#{@collection.name}/#{name}"
- else
- "#{@collection.name}"
- end
- end
- end
-
- def generate
- Rabbit::routes << [@method, "#{settings.root_url}/#{path}"]
- ::Sinatra::Application.send(@method, "#{settings.root_url}/#{path}", {}, &@control)
- # Set up some Rails-like URL helpers
- if name == :index
- gen_route "#{@collection.name}_url"
- elsif name == :show
- gen_route "#{@collection.name.to_s.singularize}_url"
- else
- gen_route "#{name}_#{@collection.name.to_s.singularize}_url"
- end
- end
-
- # Return a hash of all params, the params statically defined for this
- # operation plus the params defined by any features in the +driver+
- # that might modify this operation
- def effective_params(driver)
- driver.features(@collection.name).collect do |f|
- f.decl.operation(@name)
- end.flatten.select { |op| op }.inject(params.dup) do |result, fop|
- fop.params.each_key do |k|
- if result.has_key?(k)
- raise DuplicateParamException, "Parameter '#{k}' for operation #{fop.name} in collection #{@collection.name}"
- else
- result[k] = fop.params[k]
- end
- end
- result
- end
- end
-
- private
- def gen_route(name)
- route_url = path
- if @member
- ::Sinatra::Application.send(:define_method, name) do |id, *args|
- url = query_url(route_url, args[0])
- api_url_for url.gsub(/:id/, id.to_s), :full
- end
- else
- ::Sinatra::Application.send(:define_method, name) do |*args|
- url = query_url(route_url, args[0])
- api_url_for url, :full
- end
- end
- end
- end
-
- class Collection
- attr_reader :name, :operations, :subcollections
-
- def initialize(name, options={}, &block)
- @name = name
- @description = ""
- @operations, @subcollections = {}, {}
- @global = options[:global] || false
- instance_eval(&block) if block_given?
- generate_documentation
- generate_head
- generate_options
- end
-
- def subcollection?
- self.class == SubCollection
- end
-
- # Set/Return description for collection
- # If first parameter is not present, full description will be
- # returned.
- def description(text='')
- return @description if text.blank?
- @description = text
- end
-
- # Mark this collection as global, i.e. independent of any specific
- # driver
- def global!
- @global = true
- end
-
- # Return +true+ if this collection is global, i.e. independent of any
- # specific driver
- def global?
- @global
- end
-
- def generate_head
- current_collection = self
- Rabbit::routes << [:head, "#{settings.root_url}/#{name}"]
- ::Sinatra::Application.head("#{settings.root_url}/#{name}") do
- methods_allowed = current_collection.operations.collect { |o| o[1].method.to_s.upcase }.uniq.join(',')
- headers 'Allow' => "HEAD,OPTIONS,#{methods_allowed}"
- [200, '']
- end
- end
-
- def generate_options
- current_collection = self
- Rabbit::routes << [:options, "#{settings.root_url}/#{name}"]
- ::Sinatra::Application.options("#{settings.root_url}/#{name}") do
- operations_allowed = current_collection.operations.collect { |o| o[0] }.join(',')
- headers 'X-Operations-Allowed' => operations_allowed
- [200, '']
- end
- end
-
- def generate_documentation
- coll = self
- Rabbit::routes << [:get, "#{settings.root_url}/docs/#{@name}"]
- ::Sinatra::Application.get("#{settings.root_url}/docs/#{@name}") do
- coll.check_supported(driver)
- @collection = coll
- @operations = coll.operations
- @features = driver.features(coll.name)
- respond_to do |format|
- format.html { haml :'docs/collection' }
- format.xml { haml :'docs/collection' }
- end
- end
- end
-
- # Add a new operation for this collection. For the standard REST
- # operations :index, :show, :update, and :destroy, we already know
- # what method to use and whether this is an operation on the URL for
- # individual elements or for the whole collection.
- #
- # For non-standard operations, options must be passed:
- # :method : one of the HTTP methods
- # :member : whether this is an operation on the collection or an
- # individual element (FIXME: custom operations on the
- # collection will use a nonsensical URL) The URL for the
- # operation is the element URL with the name of the operation
- # appended
- #
- # This also defines a helper method like show_instance_url that returns
- # the URL to this operation (in request context)
- def operation(name, opts = {}, &block)
- if @operations.keys.include?(name)
- raise DuplicateOperationException::new(500, "DuplicateOperation", "Operation #{name} is already defined", [])
- end
- @operations[name] = Operation.new(self, name, opts, &block)
- end
-
- def collection(name, opts={}, &block)
- if subcollections.keys.include?(name)
- raise DuplicateOperationException::new(500, "DuplicateSubcollection", "Subcollection #{name} is already defined", [])
- end
- subcollections[name] = SubCollection.new(self, name, opts, &block)
- subcollections[name].generate
- end
-
- def generate
- operations.values.reject { |op| op.member }.each { |o| o.generate }
- operations.values.select { |op| op.member }.each { |o| o.generate }
- app = ::Sinatra::Application
- collname = name # Work around Ruby's weird scoping/capture
- app.send(:define_method, "#{name.to_s.singularize}_url") do |id|
- api_url_for "#{collname}/#{id}", :full
- end
- if index_op = operations[:index]
- app.send(:define_method, "#{name}_url") do
- api_url_for index_op.path.gsub(/\/\?$/,''), :full
- end
- end
- end
-
- def check_supported(driver)
- unless global? || driver.has_collection?(@name) || self.kind_of?(Sinatra::Rabbit::SubCollection)
- raise UnsupportedCollectionException
- end
- end
- end
-
- class SubCollection < Collection
-
- attr_accessor :parent
-
- def initialize(parent, name, opts={}, &block)
- self.parent = parent
- super(name, &block)
- end
-
- def operation(name, opts = {}, &block)
- if @operations.keys.include?(name)
- raise DuplicateOperationException::new(500, "DuplicateOperation", "Operation #{name} is already defined", [])
- end
- # Preserve self as local variable to workaround Ruby namespace
- # weirdness
- c = self
- path_generator = Proc.new do |obj|
- if obj.member?
- if obj.standard?
- "#{parent.name}/:#{parent.name.to_s.singularize}/:#{c.name.to_s.singularize}"
- else
- "#{parent.name}/:#{parent.name.to_s.singularize}/:#{c.name.to_s.singularize}/#{name}"
- end
- else
- if obj.form?
- "#{parent.name}/:id/:#{parent.name.to_s.singularize}/#{obj.name}"
- else
- "#{parent.name}/:#{parent.name.to_s.singularize}"
- end
- end
- end
- opts.merge!({
- :path_generator => path_generator
- })
- @operations[name] = Operation.new(self, name, opts, &block)
- end
-
- def generate
- operations.values.reject { |op| op.member }.each { |o| o.generate }
- operations.values.select { |op| op.member }.each { |o| o.generate }
- app = ::Sinatra::Application
- collname = name # Work around Ruby's weird scoping/capture
- app.send(:define_method, "#{parent.name.to_s}_#{name.to_s.singularize}_url") do |id, subid|
- api_url_for "#{collname}/#{id}/#{subid}", :full
- end
- if index_op = operations[:index]
- app.send(:define_method, "#{parent.name.to_s}_#{name}_url") do
- api_url_for index_op.path.gsub(/\/\?$/,''), :full
- end
- end
- end
-
- end
-
- def collections
- @collections ||= {}
- end
-
- # Create a new collection. NAME should be the pluralized name of the
- # collection.
- #
- # Adds a helper method #{name}_url which returns the URL to the :index
- # operation on this collection.
- def collection(name, &block)
- raise DuplicateCollectionException if collections[name]
- collections[name] = Collection.new(name, &block)
- collections[name].generate
- end
-
- def global_collection(name, &block)
- raise DuplicateCollectionException if collections[name]
- collections[name] = Collection.new(name, { :global => true }, &block)
- collections[name].generate
- end
-
- # Make sure this collection can be accessed, regardless of whether the
- # driver supports it or not
- def global_collection(name, &block)
- raise DuplicateCollectionException if collections[name]
- collections[name] = Collection.new(name, :global => true, &block)
- collections[name].generate
- end
- end
-
- module RabbitHelper
- def query_url(url, params)
- return url if params.nil? || params.empty?
- url + "?#{URI.escape(params.collect{|k,v| "#{k}=#{v}"}.join('&'))}"
- end
-
- def entry_points
- collections.values.select { |coll|
- coll.global? || driver.has_collection?(coll.name)
- }.inject([]) do |m, coll|
- url = api_url_for coll.operations[:index].path, :full
- m << [ coll.name, url ]
- end
- end
- end
-
- register Rabbit
- helpers RabbitHelper
-end
-
-# In Sinatra < 1.2 there was no helper to create OPTIONS route
-unless Sinatra::Base.respond_to? :options
- configure do
- class << Sinatra::Base
- def options(path, opts={}, &block)
- route 'OPTIONS', path, opts, &block
- end
- end
- Sinatra::Delegator.delegate :options
- end
-end
diff --git a/server/lib/sinatra/rack_accept.rb b/server/lib/sinatra/rack_accept.rb
index 2ad52c7..e4a0f1f 100644
--- a/server/lib/sinatra/rack_accept.rb
+++ b/server/lib/sinatra/rack_accept.rb
@@ -12,11 +12,8 @@
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
-require 'sinatra/base'
require 'rack/accept'
-use Rack::Accept
-
module Rack
module RespondTo
@@ -26,7 +23,6 @@ module Rack
# We need to overide the default render method to supply correct path to the
# template, since Sinatra is by default looking in the current __FILE__ path
def self.registered(app)
- app.helpers Rack::RespondTo::Helpers
app.class_eval do
alias :render_without_format :render
def render(*args, &block)
diff --git a/server/lib/sinatra/rack_cimi.rb b/server/lib/sinatra/rack_cimi.rb
deleted file mode 100644
index 6d5ea78..0000000
--- a/server/lib/sinatra/rack_cimi.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-# 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 Rack
- # Automatically sets the X-CIMI-Specification-Version header on all responses.
- #
- class CIMI
-
- def initialize(app, no_cache_control = nil, cache_control = nil)
- @app = app
- end
-
- def call(env)
- status, headers, body = @app.call(env)
- headers['X-CIMI-Specification-Version'] = '0.0.66'
- [status, headers, body]
- end
-
- end
-end
-
diff --git a/server/lib/sinatra/rack_matrix_params.rb b/server/lib/sinatra/rack_matrix_params.rb
index 177e745..0d9339b 100644
--- a/server/lib/sinatra/rack_matrix_params.rb
+++ b/server/lib/sinatra/rack_matrix_params.rb
@@ -51,9 +51,7 @@ module Rack
while param=sub_components.pop do
if value
matrix_params[sub_components.first] ||= {}
- matrix_params[sub_components.first].merge!(
- param => value
- )
+ matrix_params[sub_components.first].merge!(param => value)
value=nil
next
else
diff --git a/server/lib/sinatra/rack_runtime.rb b/server/lib/sinatra/rack_runtime.rb
deleted file mode 100644
index dc56fc7..0000000
--- a/server/lib/sinatra/rack_runtime.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-# Copyright (c) 2008 The Committers
-
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to
-# deal in the Software without restriction, including without limitation the
-# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-# sell copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-# THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-module Rack
- # Sets an "X-Runtime" response header, indicating the response
- # time of the request, in seconds
- #
- # You can put it right before the application to see the processing
- # time, or before all the other middlewares to include time for them,
- # too.
- class Runtime
- def initialize(app, name = nil)
- @app = app
- @header_name = "X-Runtime"
- @header_name << "-#{name}" if name
- end
-
- def call(env)
- start_time = Time.now
- status, headers, body = @app.call(env)
- request_time = Time.now - start_time
-
- if !headers.has_key?(@header_name)
- headers[@header_name] = "%0.6f" % request_time
- end
-
- [status, headers, body]
- end
- end
-end
-
diff --git a/server/lib/sinatra/rack_syslog.rb b/server/lib/sinatra/rack_syslog.rb
deleted file mode 100644
index 5565179..0000000
--- a/server/lib/sinatra/rack_syslog.rb
+++ /dev/null
@@ -1,93 +0,0 @@
-begin
- require 'syslog'
- USE_SYSLOG = true
-rescue LoadError => e
- USE_SYSLOG = false
-end
-
-require 'sinatra/body_proxy'
-
-class SyslogFile < File
-
- def initialize
- @log = USE_SYSLOG ? Syslog.open($0, Syslog::LOG_PID | Syslog::LOG_LOCAL5) : Logger.new(STDOUT)
- end
-
- def write(string)
- @log.warning(string) if string.strip.length > 0
- return string.chars.count
- end
-
- def info(msg)
- @log.info("%s" % msg)
- end
-
- def err(msg)
- @log.err("%s" % msg)
- end
-
- alias :warning :err
-
-end
-
-# Code bellow was originaly copied from Rack::CommonLogger
-# https://raw.github.com/rack/rack/master/lib/rack/commonlogger.rb
-
-module Rack
- # Rack::CommonLogger forwards every request to an +app+ given, and
- # logs a line in the Apache common log format to the +logger+, or
- # rack.errors by default.
- class SyslogLogger
-
- # Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common
- # lilith.local - - [07/Aug/2006 23:58:02] "GET / HTTP/1.1" 500 -
- # %{%s - %s [%s] "%s %s%s %s" %d %s\n} %
- FORMAT = %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f}
-
- def initialize(app, logger=nil)
- @app = app
- @logger = logger || @app.settings.logger || $stdout
- end
-
- def call(env)
- began_at = Time.now
- status, header, body = @app.call(env)
- header = Utils::HeaderHash.new(header)
- body = Rack::BodyProxy.new(body) do
- log(env, status, header, began_at)
- end
- body.close
- [status, header, body]
- end
-
- def log(env, status, header, began_at)
- now = Time.now
- length = extract_content_length(header)
-
- if status.to_s =~ /5(\d{2})/
- method = :err
- else
- method = :info
- end
-
- logger = @logger
- logger.send(method, FORMAT % [
- env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
- env["REMOTE_USER"] || "-",
- now.strftime("%d/%b/%Y %H:%M:%S"),
- env["REQUEST_METHOD"],
- env["PATH_INFO"],
- env["QUERY_STRING"].empty? ? "" : "?"+env["QUERY_STRING"],
- env["HTTP_VERSION"],
- status.to_s[0..3],
- length,
- now - began_at ])
- end
-
- def extract_content_length(headers)
- value = headers['Content-Length'] or return '-'
- value.to_s == '0' ? '-' : value
- end
- end
-end
-
diff --git a/server/lib/sinatra/sinatra_verbose.rb b/server/lib/sinatra/sinatra_verbose.rb
deleted file mode 100644
index c016ec0..0000000
--- a/server/lib/sinatra/sinatra_verbose.rb
+++ /dev/null
@@ -1,73 +0,0 @@
-#
-# 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.
-
-require 'sinatra/base'
-
-module Sinatra
- module VerboseLogger
-
- module Helpers
-
- def info(message)
- puts sprintf("\033[1;34m[INFO: #{caller_method_name}]\033[0m: %s", message.inspect)
- end
-
- alias :debug :info
-
- def warn(message)
- puts sprintf("\033[1;31m[WARN: #{caller_method_name}]\033[0m: %s", message.inspect)
- end
-
- private
-
- def caller_method_name
- caller(2).first
- end
-
- end
-
- def enable_verbose_logging!
- disable :logging
- before {
- puts sprintf("\n\033[1;29mProcessing %s\033[0m (for %s at #{Time.now}) [%s] [\033[1;29m%s\033[0m]",
- request.path_info, request.ip, request.request_method, driver_name)
- puts "Parameters: #{params.inspect}"
- if provider=Thread::current[:provider] || ENV['API_PROVIDER']
- puts "Provider: #{provider}"
- end
- puts "Authentication: #{request.env['HTTP_AUTHORIZATION'].split(' ').first}" if request.env['HTTP_AUTHORIZATION']
- puts "Server: #{request.env['SERVER_SOFTWARE']}"
- puts "Accept: #{request.env['HTTP_ACCEPT']}"
- puts
- }
- after {
- puts sprintf("\nCompleted in \033[1;29m%4f\033[0m | %4f | %s | \033[1;36m%s\033[0m | %s\n",
- response.header['X-Backend-Runtime'] || 0, response.header['X-Runtime'] || 0, response.status, response.content_type, request.url)
- }
- end
-
- def self.registered(app)
- app.helpers VerboseLogger::Helpers
- app.enable_verbose_logging! if ENV['API_VERBOSE']
- end
- end
-end
-
-Sinatra::Application.register Sinatra::VerboseLogger
-
-Deltacloud::BaseDriver.class_eval do
- include Sinatra::VerboseLogger::Helpers
-end
diff --git a/server/lib/sinatra/static_assets.rb b/server/lib/sinatra/static_assets.rb
deleted file mode 100644
index 5233965..0000000
--- a/server/lib/sinatra/static_assets.rb
+++ /dev/null
@@ -1,99 +0,0 @@
-#
-# 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.
-
-require 'sinatra/base'
-require 'sinatra/url_for'
-
-module Sinatra
- module StaticAssets
- module Helpers
- # In HTML <link> and <img> tags have no end tag.
- # In XHTML, on the contrary, these tags must be properly closed.
- #
- # We can choose the appropriate behaviour with +closed+ option:
- #
- # image_tag "/images/foo.png", :alt => "Foo itself", :closed => true
- #
- # The default value of +closed+ option is +false+.
- #
- def image_tag(source, options = {})
- options[:src] = url_for(source)
- tag("img", options)
- end
-
- def stylesheet_link_tag(*sources)
- list, options = extract_options(sources)
- list.collect { |source| stylesheet_tag(source, options) }.join("\n")
- end
-
- def javascript_script_tag(*sources)
- list, options = extract_options(sources)
- list.collect { |source| javascript_tag(source, options) }.join("\n")
- end
-
- def link_to(desc, url, options = {})
- tag("a", options.merge(:href => url_for(url))) do
- desc
- end
- end
-
- private
-
- def tag(name, local_options = {})
- start_tag = "<#{name}#{tag_options(local_options) if local_options}"
- if block_given?
- content = yield
- "#{start_tag}>#{content}</#{name}>"
- else
- "#{start_tag}#{"/" if settings.xhtml}>"
- end
- end
-
- def tag_options(options)
- unless options.empty?
- attrs = []
- attrs = options.map { |key, value| %(#{key}="#{Rack::Utils.escape_html(value)}") }
- " #{attrs.sort * ' '}" unless attrs.empty?
- end
- end
-
- def stylesheet_tag(source, options = {})
- tag("link", { :type => "text/css",
- :charset => "utf-8", :media => "screen", :rel => "stylesheet",
- :href => url_for(source) }.merge(options))
- end
-
- def javascript_tag(source, options = {})
- tag("script", { :type => "text/javascript", :charset => "utf-8",
- :src => url_for(source) }.merge(options)) do
- end
- end
-
- def extract_options(a)
- opts = a.last.is_a?(::Hash) ? a.pop : {}
- [a, opts]
- end
-
- end
-
- def self.registered(app)
- app.helpers StaticAssets::Helpers
- app.disable :xhtml
- end
- end
-
- register StaticAssets
-end
diff --git a/server/lib/sinatra/url_for.rb b/server/lib/sinatra/url_for.rb
deleted file mode 100644
index fba6668..0000000
--- a/server/lib/sinatra/url_for.rb
+++ /dev/null
@@ -1,93 +0,0 @@
-#
-# Based on https://github.com/emk/sinatra-url-for/
-# Commit 1df339284203f8f6ed8d
-#
-# Original license:
-# Copyright (C) 2009 Eric Kidd
-#
-# Permission is hereby granted, free of charge, to any person obtaining a
-# copy of this software and associated documentation files (the
-# "Software"), to deal in the Software without restriction, including
-# without limitation the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the Software, and to permit
-# persons to whom the Software is furnished to do so, subject to the
-# following conditions:
-#
-# The above copyright notice and this permission notice shall be included
-# in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
-# NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
-# USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-require 'uri'
-
-module Sinatra
- module UrlForHelper
-
- def api_url_for(url_fragment, mode=:path_only)
- matrix_params = ''
- if request.params['api']
- matrix_params += ";provider=%s" % request.params['api']['provider'] if request.params['api']['provider']
- matrix_params += ";driver=%s" % request.params['api']['driver'] if request.params['api']['driver']
- end
- url_fragment = "/#{url_fragment}" unless url_fragment =~ /^\// # There is no need to prefix URI with '/'
- url_for "#{settings.root_url}#{matrix_params}#{url_fragment}", mode
- end
-
- # Construct a link to +url_fragment+, which should be given relative to
- # the base of this Sinatra app. The mode should be either
- # <code>:path_only</code>, which will generate an absolute path within
- # the current domain (the default), or <code>:full</code>, which will
- # include the site name and port number. (The latter is typically
- # necessary for links in RSS feeds.) Example usage:
- #
- # url_for "/" # Returns "/myapp/"
- # url_for "/foo" # Returns "/myapp/foo"
- # url_for "/foo", :full # Returns "http://example.com/myapp/foo"
- #--
- # See README.rdoc for a list of some of the people who helped me clean
- # up earlier versions of this code.
- def url_for url_fragment, mode=:path_only
- case mode
- when :path_only
- base = request.script_name
- when :full
- scheme = request.scheme
- port = request.port
- request_host = request.host
- if request.env['HTTP_X_FORWARDED_FOR']
- scheme = request.env['HTTP_X_FORWARDED_SCHEME'] || scheme
- port = request.env['HTTP_X_FORWARDED_PORT']
- request_host = request.env['HTTP_X_FORWARDED_HOST']
- end
- if (port.nil? || port == "" ||
- (scheme == 'http' && port.to_s == '80') ||
- (scheme == 'https' && port.to_s == '443'))
- port = ""
- else
- port = ":#{port}"
- end
- base = "#{scheme}://#{request_host}#{port}#{request.script_name}"
- else
- raise TypeError, "Unknown url_for mode #{mode}"
- end
- url_escape = URI.escape(url_fragment)
- # Don't add the base fragment if url_for gets called more than once
- # per url or the url_fragment passed in is an absolute url
- if url_escape.match(/^#{base}/) or url_escape.match(/^http/)
- url_escape
- else
- "#{base}#{url_escape}"
- end
- end
- end
-
-
-
- helpers UrlForHelper
-end
diff --git a/server/views/addresses/show.html.haml b/server/views/addresses/show.html.haml
index b646bf4..ab4b4e2 100644
--- a/server/views/addresses/show.html.haml
+++ b/server/views/addresses/show.html.haml
@@ -13,7 +13,7 @@
%li
%div{ :'data-role' => 'controlgroup', :'data-type' => "horizontal" }
- if @address.associated?
- =link_to @address.instance_id, instance_url(@address.instance_id)
+ %a{:href => instance_url(@address.instance_id) }=@address.instance_id
=link_to_action 'Disassociate', disassociate_address_url(@address.id), :post
- else
- if driver.respond_to?(:associate_address)
diff --git a/server/views/api/show.html.haml b/server/views/api/show.html.haml
index 265739d..9a40860 100644
--- a/server/views/api/show.html.haml
+++ b/server/views/api/show.html.haml
@@ -3,8 +3,11 @@
%div{ :'data-role' => :content, :'data-theme' => 'c'}
%ul{ :'data-role' => :listview, :'data-inset' => 'true'}
- - @collections.sort_by { |k| k.to_s }.each do |key|
- %li= link_to key.to_s.gsub('_', ' ').titlecase, api_url_for(key), :'data-icon' => "arrow-r", :'data-ajax' => false
+
+ - Deltacloud.collections.each do |c|
+ - next unless c.operation(:index).has_capability?
+ %li
+ %a{ :href => api_url_for(c.collection_name), :'data-icon' => "arrow-r"}=c.collection_name.to_s.gsub('_', ' ').titlecase
- if @providers.size > 1
%div{ :'data-role' => :footer, :'data-theme' => 'a'}
diff --git a/server/views/error.html.haml b/server/views/error.html.haml
index 4911e28..92897e1 100644
--- a/server/views/error.html.haml
+++ b/server/views/error.html.haml
@@ -2,8 +2,8 @@
%html
%head
%title Deltacloud API #{settings.version}
- = stylesheet_link_tag '/stylesheets/jquery.mobile-1.0.1.min.css'
- = stylesheet_link_tag '/stylesheets/new.css'
+ %link{ :charset => "utf-8", :href => "/stylesheets/jquery.mobile-1.0.1.min.css", :media => "screen", :rel => "stylesheet", :type => "text/css"}
+ %link{ :charset => "utf-8", :href => "/stylesheets/new.css", :media => "screen", :rel => "stylesheet", :type => "text/css"}
%script{:type => "text/javascript", :src => "/javascripts/jquery.min.js" }
%script{:type => "text/javascript", :src => "/javascripts/application.js" }
%script{:type => "text/javascript", :src => "/javascripts/jquery.mobile-1.0.1.min.js" }
diff --git a/server/views/instances/new.html.haml b/server/views/instances/new.html.haml
index 2347022..5ca775c 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, :'data-ajax' => 'false'}
%input{ :name => :image_id, :type => :hidden, :value => @instance.image_id }/
- - if driver_has_feature?(:user_name)
+ - if driver.class.has_feature?(:instances, :user_name)
%div{ 'data-role' => :fieldcontain }
%label{ :for => :name} Instance name:
%input{ :type => :text, :id => :name, :name => :name, :value => '' }
@@ -29,19 +29,19 @@
%div{ 'data-role' => :collapsible, 'data-collapsed' => "true"}
%h3 Additional parameters
- - if driver_has_feature?(:user_data)
+ - if driver.class.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_has_feature?(:instance_count)
+ - if driver.class.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_has_feature?(:authentication_key)
+ - if driver.class.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" }
@@ -49,7 +49,7 @@
- @keys.each do |key|
%option{ :value => key.id } #{key.id}
- - if driver_has_feature?(:register_to_load_balancer)
+ - if driver.class.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" }
@@ -57,7 +57,7 @@
- @load_balancers.each do |load_balancer|
%option{:value => load_balancer.id} #{load_balancer.id}
- - if driver_has_feature?(:firewalls)
+ - if driver.class.has_feature?(:instances, :firewalls)
%div{ 'data-role' => :fieldcontain }
%fieldset{ :'data-role' => 'controlgroup'}
%legend Register to firewall:
diff --git a/server/views/instances/show.xml.haml b/server/views/instances/show.xml.haml
index b2fd5bf..3a62ed3 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_has_auth_features?
+ - if driver.class.has_feature?(:authentication_key) or driver.class.has_feature?(:authentication_password)
%authentication{ :type => driver_auth_feature_name }
- if @instance.authn_feature_failed?
%error #{@instance.authn_error}
diff --git a/server/views/layout.html.haml b/server/views/layout.html.haml
index bef167d..639c618 100644
--- a/server/views/layout.html.haml
+++ b/server/views/layout.html.haml
@@ -2,16 +2,14 @@
%html
%head
%title Deltacloud API #{settings.version}
- = stylesheet_link_tag '/stylesheets/jquery.mobile-1.0.1.min.css'
- = stylesheet_link_tag '/stylesheets/new.css'
+ %link{ :charset => "utf-8", :href => "/stylesheets/jquery.mobile-1.0.1.min.css", :media => "screen", :rel => "stylesheet", :type => "text/css"}
+ %link{ :charset => "utf-8", :href => "/stylesheets/new.css", :media => "screen", :rel => "stylesheet", :type => "text/css"}
%script{:type => "text/javascript", :src => "/javascripts/jquery.min.js" }
%script{:type => "text/javascript", :src => "/javascripts/application.js" }
%script{:type => "text/javascript", :src => "/javascripts/jquery.mobile-1.0.1.min.js" }
:javascript
$(document).bind("mobileinit", function(){
- $.mobile.ajaxEnabled = false;
$.mobile.hashListeningEnabled = false;
- $.mobile.selectmenu.prototype.options.nativeMenu = false;
})
%body
%div{ 'data-role' => :page }
diff --git a/server/views/root/index.html.haml b/server/views/root/index.html.haml
index c102a59..cc2d36e 100644
--- a/server/views/root/index.html.haml
+++ b/server/views/root/index.html.haml
@@ -1,4 +1,4 @@
%h1
Deltacloud:
= DRIVER.to_s.titlecase
-= link_to 'API', api_url
+%a{ :href => api_url} API
--
1.7.10.1
[PATCH core 13/26] Core: CIMI helper is now not longer registred using Sinatra::Application
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/lib/cimi/helpers/cimi_helper.rb | 2 --
1 file changed, 2 deletions(-)
diff --git a/server/lib/cimi/helpers/cimi_helper.rb b/server/lib/cimi/helpers/cimi_helper.rb
index b3f3493..e48fa8e 100644
--- a/server/lib/cimi/helpers/cimi_helper.rb
+++ b/server/lib/cimi/helpers/cimi_helper.rb
@@ -28,8 +28,6 @@ module CIMIHelper
end
-helpers CIMIHelper
-
class Array
def to_xml_cimi_collection(_self)
model_name = first.class.xml_tag_name
--
1.7.10.1
Re: Modular Deltacloud (rev 2)
Posted by Michal Fojtik <mf...@redhat.com>.
On 05/17/12, David Lutterkort wrote:
Thanks! I applied the patches and both tasks now run fine under 1.8.
I'll continue with swithing the rest of unit tests we have.
-- Michal
> On Wed, 2012-05-16 at 15:18 +0200, Michal Fojtik wrote:
> > On 05/15/12, Michal Fojtik wrote:
>
> > === Work with Sinatra::Base and should be all GREEN ===
> >
> > $ rake test:mock # => Minitest for Mock drivers and the overall API
> > $ rake cucumber:ec2:test # => EC2 Cucumber scenarios
> > $ rake cucumber:mock:test # => Mock Cucumber scenarios
> > $ rake cimi # => CIMI cucumber scenarios
>
> This fails for me because it can't find
> lib/deltacloud/helpers/assets_helper - did you forget to check that in ?
>
> > $ rake spec # => CIMI models rspec tests
>
> I had to add a few patches of my own to make that work (attached) ;)
> Just fold them into yours.
>
> I now get another test failure:
> Failure/Error: should_properly_serialize_model CIMI::Model::MachineImage, @xml, @json
> XML documents do not match
> entries differ[/MachineImage[0]/property[0]/name]: "status" != "locked"
> entries differ[/MachineImage[0]/property[0]/content]: "BUILD" != "true"
> entries differ[/MachineImage[0]/property[1]/name]: "locked" != "status"
> entries differ[/MachineImage[0]/property[1]/content]: "true" != "BUILD"
>
> This is because in 1.8, Hash has no order; we could import
> ActiveSupport::OrderedHash to achieve this (or only use data for the
> CIMI tests that has at most one property)
>
> David
>
--
Michal Fojtik
Sr. Software Engineer, Deltacloud API (http://deltacloud.org)
Re: Modular Deltacloud (rev 2)
Posted by David Lutterkort <lu...@redhat.com>.
On Wed, 2012-05-16 at 15:18 +0200, Michal Fojtik wrote:
> On 05/15/12, Michal Fojtik wrote:
> === Work with Sinatra::Base and should be all GREEN ===
>
> $ rake test:mock # => Minitest for Mock drivers and the overall API
> $ rake cucumber:ec2:test # => EC2 Cucumber scenarios
> $ rake cucumber:mock:test # => Mock Cucumber scenarios
> $ rake cimi # => CIMI cucumber scenarios
This fails for me because it can't find
lib/deltacloud/helpers/assets_helper - did you forget to check that in ?
> $ rake spec # => CIMI models rspec tests
I had to add a few patches of my own to make that work (attached) ;)
Just fold them into yours.
I now get another test failure:
Failure/Error: should_properly_serialize_model CIMI::Model::MachineImage, @xml, @json
XML documents do not match
entries differ[/MachineImage[0]/property[0]/name]: "status" != "locked"
entries differ[/MachineImage[0]/property[0]/content]: "BUILD" != "true"
entries differ[/MachineImage[0]/property[1]/name]: "locked" != "status"
entries differ[/MachineImage[0]/property[1]/content]: "true" != "BUILD"
This is because in 1.8, Hash has no order; we could import
ActiveSupport::OrderedHash to achieve this (or only use data for the
CIMI tests that has at most one property)
David
Re: Modular Deltacloud (rev 2)
Posted by Michal Fojtik <mf...@redhat.com>.
On 05/15/12, Michal Fojtik wrote:
As you may check, I add some more patches to fix various incompatibilities
in our testing frameworks. So currently, this is the status:
=== Work with Sinatra::Base and should be all GREEN ===
$ rake test:mock # => Minitest for Mock drivers and the overall API
$ rake cucumber:ec2:test # => EC2 Cucumber scenarios
$ rake cucumber:mock:test # => Mock Cucumber scenarios
$ rake cimi # => CIMI cucumber scenarios
$ rake spec # => CIMI models rspec tests
=== Work with Sinatra::Base with errors/failures ===
$ rake spec (in client/) # => Ruby client rspec (1 FAILURE)
-> The failure is related to buckets, need to investigate.
$ rake cucumber:sbc:test # => Mock Cucumber scenarios
-> Several failures, seems like these tests need to be re-recorded.
=== Work in progress ===
$ rake test:openstack
$ rake test:rhevm
To track all changes pls. use https://github.com/mifo/deltacloud repo.
Note that there are more than 35 patches now and some of them will be
sqeezed in rebase :-)
-- michal
> On 05/15/12, mfojtik@redhat.com wrote:
>
> I forgot, that for those who don't like to download all mails and apply
> them because then don't have awesome mutt macro, there is a github mirror
>
> https://github.com/mifo/deltacloud
>
> The patches there are applied on top of current DC master and the repo
> should be able to be rebased against master.
>
> -- michal
>
> > Hi,
> >
> > I tried hard to shrink the patches and ended up with number 21 ;-)
> > I hope that will make it more easy to review.
> >
> > This revision also feature fixed cucumber and mock unit tests:
> >
> > $ cd core/server
> > $ rake cucumber:mock:test
> > $ rake test:mock
> >
> > The EC2 version of Cucumber should work too, but I have some strange
> > problem with VCR I need to figure out.
> >
> > -- Michal
> >
>
> --
> Michal Fojtik
> Sr. Software Engineer, Deltacloud API (http://deltacloud.org)
--
Michal Fojtik
Sr. Software Engineer, Deltacloud API (http://deltacloud.org)
Re: Modular Deltacloud (rev 2)
Posted by Michal Fojtik <mf...@redhat.com>.
On 05/15/12, mfojtik@redhat.com wrote:
I forgot, that for those who don't like to download all mails and apply
them because then don't have awesome mutt macro, there is a github mirror
https://github.com/mifo/deltacloud
The patches there are applied on top of current DC master and the repo
should be able to be rebased against master.
-- michal
> Hi,
>
> I tried hard to shrink the patches and ended up with number 21 ;-)
> I hope that will make it more easy to review.
>
> This revision also feature fixed cucumber and mock unit tests:
>
> $ cd core/server
> $ rake cucumber:mock:test
> $ rake test:mock
>
> The EC2 version of Cucumber should work too, but I have some strange
> problem with VCR I need to figure out.
>
> -- Michal
>
--
Michal Fojtik
Sr. Software Engineer, Deltacloud API (http://deltacloud.org)
[PATCH core 12/26] CIMI: Moved code from CIMI server.rb and split it to smaller collections like in Deltacloud
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/lib/cimi/collections.rb | 58 ++
server/lib/cimi/collections/address_templates.rb | 49 +
server/lib/cimi/collections/addresses.rb | 74 ++
server/lib/cimi/collections/cloud_entry_point.rb | 32 +
server/lib/cimi/collections/entity_metadata.rb | 48 +
server/lib/cimi/collections/machine_admins.rb | 74 ++
.../lib/cimi/collections/machine_configurations.rb | 49 +
server/lib/cimi/collections/machine_images.rb | 50 +
server/lib/cimi/collections/machines.rb | 157 ++++
.../lib/cimi/collections/network_configurations.rb | 47 +
server/lib/cimi/collections/network_templates.rb | 48 +
server/lib/cimi/collections/networks.rb | 125 +++
.../cimi/collections/routing_group_templates.rb | 47 +
server/lib/cimi/collections/routing_groups.rb | 48 +
.../lib/cimi/collections/volume_configurations.rb | 48 +
server/lib/cimi/collections/volume_images.rb | 50 +
server/lib/cimi/collections/volumes.rb | 80 ++
server/lib/cimi/collections/vsp_configurations.rb | 48 +
server/lib/cimi/collections/vsp_templates.rb | 50 +
server/lib/cimi/collections/vsps.rb | 108 +++
server/lib/cimi/helpers.rb | 104 +++
server/lib/cimi/model.rb | 67 --
server/lib/cimi/model/action.rb | 24 -
server/lib/cimi/model/address.rb | 72 --
server/lib/cimi/model/address_collection.rb | 34 -
server/lib/cimi/model/address_template.rb | 54 --
.../lib/cimi/model/address_template_collection.rb | 34 -
server/lib/cimi/model/base.rb | 249 -----
server/lib/cimi/model/cloud_entry_point.rb | 48 -
server/lib/cimi/model/entity_metadata.rb | 83 --
.../lib/cimi/model/entity_metadata_collection.rb | 31 -
server/lib/cimi/model/errors.rb | 48 -
server/lib/cimi/model/machine.rb | 229 -----
server/lib/cimi/model/machine_admin.rb | 59 --
server/lib/cimi/model/machine_admin_collection.rb | 34 -
server/lib/cimi/model/machine_collection.rb | 34 -
server/lib/cimi/model/machine_configuration.rb | 70 --
.../cimi/model/machine_configuration_collection.rb | 34 -
server/lib/cimi/model/machine_image.rb | 46 -
server/lib/cimi/model/machine_image_collection.rb | 34 -
server/lib/cimi/model/machine_template.rb | 41 -
.../lib/cimi/model/machine_template_collection.rb | 34 -
server/lib/cimi/model/network.rb | 106 ---
server/lib/cimi/model/network_collection.rb | 34 -
server/lib/cimi/model/network_configuration.rb | 49 -
.../cimi/model/network_configuration_collection.rb | 34 -
server/lib/cimi/model/network_template.rb | 36 -
.../lib/cimi/model/network_template_collection.rb | 35 -
server/lib/cimi/model/routing_group.rb | 34 -
server/lib/cimi/model/routing_group_collection.rb | 34 -
server/lib/cimi/model/routing_group_template.rb | 34 -
.../model/routing_group_template_collection.rb | 35 -
server/lib/cimi/model/schema.rb | 277 ------
server/lib/cimi/model/volume.rb | 103 ---
server/lib/cimi/model/volume_collection.rb | 34 -
server/lib/cimi/model/volume_configuration.rb | 60 --
.../cimi/model/volume_configuration_collection.rb | 34 -
server/lib/cimi/model/volume_image.rb | 49 -
server/lib/cimi/model/volume_image_collection.rb | 34 -
server/lib/cimi/model/volume_template.rb | 23 -
.../lib/cimi/model/volume_template_collection.rb | 34 -
server/lib/cimi/model/vsp.rb | 102 ---
server/lib/cimi/model/vsp_collection.rb | 34 -
server/lib/cimi/model/vsp_configuration.rb | 40 -
.../lib/cimi/model/vsp_configuration_collection.rb | 34 -
server/lib/cimi/model/vsp_template.rb | 34 -
server/lib/cimi/model/vsp_template_collection.rb | 34 -
server/lib/cimi/models.rb | 75 ++
server/lib/cimi/models/action.rb | 24 +
server/lib/cimi/models/address.rb | 72 ++
server/lib/cimi/models/address_collection.rb | 34 +
server/lib/cimi/models/address_template.rb | 54 ++
.../lib/cimi/models/address_template_collection.rb | 34 +
server/lib/cimi/models/base.rb | 249 +++++
server/lib/cimi/models/cloud_entry_point.rb | 46 +
server/lib/cimi/models/entity_metadata.rb | 84 ++
.../lib/cimi/models/entity_metadata_collection.rb | 31 +
server/lib/cimi/models/errors.rb | 48 +
server/lib/cimi/models/machine.rb | 227 +++++
server/lib/cimi/models/machine_admin.rb | 59 ++
server/lib/cimi/models/machine_admin_collection.rb | 34 +
server/lib/cimi/models/machine_collection.rb | 34 +
server/lib/cimi/models/machine_configuration.rb | 70 ++
.../models/machine_configuration_collection.rb | 34 +
server/lib/cimi/models/machine_image.rb | 46 +
server/lib/cimi/models/machine_image_collection.rb | 34 +
server/lib/cimi/models/machine_template.rb | 41 +
.../lib/cimi/models/machine_template_collection.rb | 34 +
server/lib/cimi/models/network.rb | 109 +++
server/lib/cimi/models/network_collection.rb | 34 +
server/lib/cimi/models/network_configuration.rb | 49 +
.../models/network_configuration_collection.rb | 34 +
server/lib/cimi/models/network_template.rb | 36 +
.../lib/cimi/models/network_template_collection.rb | 35 +
server/lib/cimi/models/routing_group.rb | 34 +
server/lib/cimi/models/routing_group_collection.rb | 34 +
server/lib/cimi/models/routing_group_template.rb | 34 +
.../models/routing_group_template_collection.rb | 35 +
server/lib/cimi/models/schema.rb | 277 ++++++
server/lib/cimi/models/volume.rb | 103 +++
server/lib/cimi/models/volume_collection.rb | 34 +
server/lib/cimi/models/volume_configuration.rb | 60 ++
.../cimi/models/volume_configuration_collection.rb | 34 +
server/lib/cimi/models/volume_image.rb | 49 +
server/lib/cimi/models/volume_image_collection.rb | 34 +
server/lib/cimi/models/volume_template.rb | 23 +
.../lib/cimi/models/volume_template_collection.rb | 34 +
server/lib/cimi/models/vsp.rb | 102 +++
server/lib/cimi/models/vsp_collection.rb | 34 +
server/lib/cimi/models/vsp_configuration.rb | 40 +
.../cimi/models/vsp_configuration_collection.rb | 34 +
server/lib/cimi/models/vsp_template.rb | 34 +
server/lib/cimi/models/vsp_template_collection.rb | 34 +
server/lib/cimi/server.rb | 959 +-------------------
114 files changed, 4148 insertions(+), 3645 deletions(-)
create mode 100644 server/lib/cimi/collections.rb
create mode 100644 server/lib/cimi/collections/address_templates.rb
create mode 100644 server/lib/cimi/collections/addresses.rb
create mode 100644 server/lib/cimi/collections/cloud_entry_point.rb
create mode 100644 server/lib/cimi/collections/entity_metadata.rb
create mode 100644 server/lib/cimi/collections/machine_admins.rb
create mode 100644 server/lib/cimi/collections/machine_configurations.rb
create mode 100644 server/lib/cimi/collections/machine_images.rb
create mode 100644 server/lib/cimi/collections/machines.rb
create mode 100644 server/lib/cimi/collections/network_configurations.rb
create mode 100644 server/lib/cimi/collections/network_templates.rb
create mode 100644 server/lib/cimi/collections/networks.rb
create mode 100644 server/lib/cimi/collections/routing_group_templates.rb
create mode 100644 server/lib/cimi/collections/routing_groups.rb
create mode 100644 server/lib/cimi/collections/volume_configurations.rb
create mode 100644 server/lib/cimi/collections/volume_images.rb
create mode 100644 server/lib/cimi/collections/volumes.rb
create mode 100644 server/lib/cimi/collections/vsp_configurations.rb
create mode 100644 server/lib/cimi/collections/vsp_templates.rb
create mode 100644 server/lib/cimi/collections/vsps.rb
create mode 100644 server/lib/cimi/helpers.rb
delete mode 100644 server/lib/cimi/model.rb
delete mode 100644 server/lib/cimi/model/action.rb
delete mode 100644 server/lib/cimi/model/address.rb
delete mode 100644 server/lib/cimi/model/address_collection.rb
delete mode 100644 server/lib/cimi/model/address_template.rb
delete mode 100644 server/lib/cimi/model/address_template_collection.rb
delete mode 100644 server/lib/cimi/model/base.rb
delete mode 100644 server/lib/cimi/model/cloud_entry_point.rb
delete mode 100644 server/lib/cimi/model/entity_metadata.rb
delete mode 100644 server/lib/cimi/model/entity_metadata_collection.rb
delete mode 100644 server/lib/cimi/model/errors.rb
delete mode 100644 server/lib/cimi/model/machine.rb
delete mode 100644 server/lib/cimi/model/machine_admin.rb
delete mode 100644 server/lib/cimi/model/machine_admin_collection.rb
delete mode 100644 server/lib/cimi/model/machine_collection.rb
delete mode 100644 server/lib/cimi/model/machine_configuration.rb
delete mode 100644 server/lib/cimi/model/machine_configuration_collection.rb
delete mode 100644 server/lib/cimi/model/machine_image.rb
delete mode 100644 server/lib/cimi/model/machine_image_collection.rb
delete mode 100644 server/lib/cimi/model/machine_template.rb
delete mode 100644 server/lib/cimi/model/machine_template_collection.rb
delete mode 100644 server/lib/cimi/model/network.rb
delete mode 100644 server/lib/cimi/model/network_collection.rb
delete mode 100644 server/lib/cimi/model/network_configuration.rb
delete mode 100644 server/lib/cimi/model/network_configuration_collection.rb
delete mode 100644 server/lib/cimi/model/network_template.rb
delete mode 100644 server/lib/cimi/model/network_template_collection.rb
delete mode 100644 server/lib/cimi/model/routing_group.rb
delete mode 100644 server/lib/cimi/model/routing_group_collection.rb
delete mode 100644 server/lib/cimi/model/routing_group_template.rb
delete mode 100644 server/lib/cimi/model/routing_group_template_collection.rb
delete mode 100644 server/lib/cimi/model/schema.rb
delete mode 100644 server/lib/cimi/model/volume.rb
delete mode 100644 server/lib/cimi/model/volume_collection.rb
delete mode 100644 server/lib/cimi/model/volume_configuration.rb
delete mode 100644 server/lib/cimi/model/volume_configuration_collection.rb
delete mode 100644 server/lib/cimi/model/volume_image.rb
delete mode 100644 server/lib/cimi/model/volume_image_collection.rb
delete mode 100644 server/lib/cimi/model/volume_template.rb
delete mode 100644 server/lib/cimi/model/volume_template_collection.rb
delete mode 100644 server/lib/cimi/model/vsp.rb
delete mode 100644 server/lib/cimi/model/vsp_collection.rb
delete mode 100644 server/lib/cimi/model/vsp_configuration.rb
delete mode 100644 server/lib/cimi/model/vsp_configuration_collection.rb
delete mode 100644 server/lib/cimi/model/vsp_template.rb
delete mode 100644 server/lib/cimi/model/vsp_template_collection.rb
create mode 100644 server/lib/cimi/models.rb
create mode 100644 server/lib/cimi/models/action.rb
create mode 100644 server/lib/cimi/models/address.rb
create mode 100644 server/lib/cimi/models/address_collection.rb
create mode 100644 server/lib/cimi/models/address_template.rb
create mode 100644 server/lib/cimi/models/address_template_collection.rb
create mode 100644 server/lib/cimi/models/base.rb
create mode 100644 server/lib/cimi/models/cloud_entry_point.rb
create mode 100644 server/lib/cimi/models/entity_metadata.rb
create mode 100644 server/lib/cimi/models/entity_metadata_collection.rb
create mode 100644 server/lib/cimi/models/errors.rb
create mode 100644 server/lib/cimi/models/machine.rb
create mode 100644 server/lib/cimi/models/machine_admin.rb
create mode 100644 server/lib/cimi/models/machine_admin_collection.rb
create mode 100644 server/lib/cimi/models/machine_collection.rb
create mode 100644 server/lib/cimi/models/machine_configuration.rb
create mode 100644 server/lib/cimi/models/machine_configuration_collection.rb
create mode 100644 server/lib/cimi/models/machine_image.rb
create mode 100644 server/lib/cimi/models/machine_image_collection.rb
create mode 100644 server/lib/cimi/models/machine_template.rb
create mode 100644 server/lib/cimi/models/machine_template_collection.rb
create mode 100644 server/lib/cimi/models/network.rb
create mode 100644 server/lib/cimi/models/network_collection.rb
create mode 100644 server/lib/cimi/models/network_configuration.rb
create mode 100644 server/lib/cimi/models/network_configuration_collection.rb
create mode 100644 server/lib/cimi/models/network_template.rb
create mode 100644 server/lib/cimi/models/network_template_collection.rb
create mode 100644 server/lib/cimi/models/routing_group.rb
create mode 100644 server/lib/cimi/models/routing_group_collection.rb
create mode 100644 server/lib/cimi/models/routing_group_template.rb
create mode 100644 server/lib/cimi/models/routing_group_template_collection.rb
create mode 100644 server/lib/cimi/models/schema.rb
create mode 100644 server/lib/cimi/models/volume.rb
create mode 100644 server/lib/cimi/models/volume_collection.rb
create mode 100644 server/lib/cimi/models/volume_configuration.rb
create mode 100644 server/lib/cimi/models/volume_configuration_collection.rb
create mode 100644 server/lib/cimi/models/volume_image.rb
create mode 100644 server/lib/cimi/models/volume_image_collection.rb
create mode 100644 server/lib/cimi/models/volume_template.rb
create mode 100644 server/lib/cimi/models/volume_template_collection.rb
create mode 100644 server/lib/cimi/models/vsp.rb
create mode 100644 server/lib/cimi/models/vsp_collection.rb
create mode 100644 server/lib/cimi/models/vsp_configuration.rb
create mode 100644 server/lib/cimi/models/vsp_configuration_collection.rb
create mode 100644 server/lib/cimi/models/vsp_template.rb
create mode 100644 server/lib/cimi/models/vsp_template_collection.rb
diff --git a/server/lib/cimi/collections.rb b/server/lib/cimi/collections.rb
new file mode 100644
index 0000000..b675fc4
--- /dev/null
+++ b/server/lib/cimi/collections.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 CIMI
+
+ def self.collection_names
+ @collections.map { |c| c.collection_name }
+ end
+
+ def self.collections
+ @collections ||= []
+ end
+
+ module Collections
+
+ def self.collection(name)
+ CIMI.collections.find { |c| c.collection_name == name }
+ end
+
+ def self.cimi_modules
+ @cimi_modules ||= []
+ end
+
+ Dir[File.join(File::dirname(__FILE__), "collections", "*.rb")].each do |collection|
+ require collection
+ base_collection_name = File.basename(collection).gsub('.rb', '')
+ cimi_module_class = CIMI::Collections.const_get(base_collection_name.camelize)
+ cimi_modules << cimi_module_class
+ unless cimi_module_class.collections.nil?
+ cimi_module_class.collections.each do |c|
+ CIMI.collections << c
+ end
+ else
+ warn "WARNING: File %s placed in collections directory but does not have any collections defined" % base_collection_name
+ end
+ end
+
+ def self.included(klass)
+ klass.class_eval do
+ CIMI::Collections.cimi_modules.each { |c| use c }
+ end
+ end
+
+ end
+end
diff --git a/server/lib/cimi/collections/address_templates.rb b/server/lib/cimi/collections/address_templates.rb
new file mode 100644
index 0000000..db59507
--- /dev/null
+++ b/server/lib/cimi/collections/address_templates.rb
@@ -0,0 +1,49 @@
+# 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 CIMI::Collections
+ class AddressTemplates < Base
+
+ check_capability :for => lambda { |m| driver.respond_to? m }
+
+ collection :address_templates do
+
+ operation :index do
+ description 'List all AddressTemplates in the AddressTemplateCollection'
+ param :CIMISelect, :string, :optional
+ control do
+ address_templates = AddressTemplateCollection.default(self).filter_by(params[:CIMISelect])
+ respond_to do |format|
+ format.xml {address_templates.to_xml}
+ format.json {address_templates.to_json}
+ end
+ end
+ end
+
+ operation :show do
+ description 'Show a specific AddressTemplate'
+ control do
+ address_template = CIMI::Model::AddressTemplate.find(params[:id], self)
+ respond_to do |format|
+ format.xml {address_template.to_xml}
+ format.json {address_template.to_json}
+ end
+ end
+ end
+
+ end
+
+ end
+end
diff --git a/server/lib/cimi/collections/addresses.rb b/server/lib/cimi/collections/addresses.rb
new file mode 100644
index 0000000..96871c1
--- /dev/null
+++ b/server/lib/cimi/collections/addresses.rb
@@ -0,0 +1,74 @@
+# 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 CIMI::Collections
+ class Addresses < Base
+
+ check_capability :for => lambda { |m| driver.respond_to? m }
+ collection :addresses do
+
+ description 'An Address represents an IP address, and its associated metdata, for a particular Network.'
+
+ operation :index do
+ description 'List all Addresses in the AddressCollection'
+ param :CIMISelect, :string, :optional
+ control do
+ addresses = AddressCollection.default(self).filter_by(params[:CIMISelect])
+ respond_to do |format|
+ format.xml {addresses.to_xml}
+ format.json {addresses.to_json}
+ end
+ end
+ end
+
+ operation :show do
+ description 'Show a specific Address'
+ control do
+ address = CIMI::Model::Address.find(params[:id], self)
+ respond_to do |format|
+ format.xml {address.to_xml}
+ format.json {address.to_json}
+ end
+ end
+ end
+
+ operation :create do
+ description "Create a new Address"
+ control do
+ if request.content_type.end_with?("json")
+ address = CIMI::Model::Address.create(request.body.read, self, :json)
+ else
+ address = CIMI::Model::Address.create(request.body.read, self, :xml)
+ end
+ respond_to do |format|
+ format.xml { address.to_xml }
+ format.json { address.to_json }
+ end
+ end
+ end
+
+ operation :destroy do
+ description "Delete a specified Address"
+ param :id, :string, :required
+ control do
+ CIMI::Model::Address.delete!(params[:id], self)
+ no_content_with_status(200)
+ end
+ end
+
+ end
+
+ end
+end
diff --git a/server/lib/cimi/collections/cloud_entry_point.rb b/server/lib/cimi/collections/cloud_entry_point.rb
new file mode 100644
index 0000000..79454f3
--- /dev/null
+++ b/server/lib/cimi/collections/cloud_entry_point.rb
@@ -0,0 +1,32 @@
+# 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 CIMI::Collections
+ class CloudEntryPoint < Base
+
+ check_capability :for => lambda { |m| driver.respond_to? m }
+
+ collection :cloudEntryPoint do
+ description 'Cloud entry point'
+ operation :index do
+ description "list all resources of the cloud"
+ control do
+ redirect API_ROOT_URL
+ end
+ end
+ end
+
+ end
+end
diff --git a/server/lib/cimi/collections/entity_metadata.rb b/server/lib/cimi/collections/entity_metadata.rb
new file mode 100644
index 0000000..84e1fec
--- /dev/null
+++ b/server/lib/cimi/collections/entity_metadata.rb
@@ -0,0 +1,48 @@
+# 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 CIMI::Collections
+ class EntityMetadata < Base
+
+ check_capability :for => lambda { |m| driver.respond_to? m }
+
+ collection :entity_metadata do
+
+ operation :index do
+ description "List all entity metadata defined for this provider"
+ control do
+ entity_metadata = CIMI::Model::EntityMetadataCollection.default(self)
+ respond_to do |format|
+ format.xml{entity_metadata.to_xml}
+ format.json{entity_metadata.to_json}
+ end
+ end
+ end
+
+ operation :show do
+ description "Get the entity metadata for a specific collection"
+ control do
+ entity_metadata = EntityMetadata.find(params[:id], self)
+ respond_to do |format|
+ format.xml{entity_metadata.to_xml}
+ format.json{entity_metadata.to_json}
+ end
+ end
+ end
+
+ end
+
+ end
+end
diff --git a/server/lib/cimi/collections/machine_admins.rb b/server/lib/cimi/collections/machine_admins.rb
new file mode 100644
index 0000000..c413cc9
--- /dev/null
+++ b/server/lib/cimi/collections/machine_admins.rb
@@ -0,0 +1,74 @@
+# 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 CIMI::Collections
+ class MachineAdmins < Base
+
+ check_capability :for => lambda { |m| driver.respond_to? m }
+
+ collection :machine_admins do
+ description 'Machine Admin entity'
+
+ operation :index do
+ description "List all machine admins"
+ param :CIMISelect, :string, :optional
+ control do
+ machine_admins = MachineAdminCollection.default(self).filter_by(params[:CIMISelect])
+ respond_to do |format|
+ format.xml { machine_admins.to_xml }
+ format.json { machine_admins.to_json }
+ end
+ end
+ end
+
+ operation :show do
+ description "Show specific machine admin"
+ control do
+ machine_admin = MachineAdmin.find(params[:id], self)
+ respond_to do |format|
+ format.xml { machine_admin.to_xml }
+ format.json { machine_admin.to_json }
+ end
+ end
+ end
+
+ operation :create do
+ description "Show specific machine admin"
+ control do
+ if request.content_type.end_with?("+json")
+ new_admin = MachineAdmin.create_from_json(request.body.read, self)
+ else
+ new_admin = MachineAdmin.create_from_xml(request.body.read, self)
+ end
+ status 201 # Created
+ respond_to do |format|
+ format.json { new_admin.to_json }
+ format.xml { new_admin.to_xml }
+ end
+ end
+ end
+
+ operation :delete do
+ description "Delete specified MachineAdmin entity"
+ control do
+ MachineAdmin.delete!(params[:id], self)
+ no_content_with_status(200)
+ end
+ end
+
+ end
+
+ end
+end
diff --git a/server/lib/cimi/collections/machine_configurations.rb b/server/lib/cimi/collections/machine_configurations.rb
new file mode 100644
index 0000000..5b09e6a
--- /dev/null
+++ b/server/lib/cimi/collections/machine_configurations.rb
@@ -0,0 +1,49 @@
+# 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 CIMI::Collections
+ class MachineConfigurations < Base
+
+ check_capability :for => lambda { |m| driver.respond_to? m }
+
+ collection :machine_configurations do
+ description 'List all machine configurations'
+
+ operation :index do
+ param :CIMISelect, :string, :optional
+ description "List all machine configurations"
+ control do
+ machine_configs = MachineConfigurationCollection.default(self).filter_by(params[:CIMISelect])
+ respond_to do |format|
+ format.xml { machine_configs.to_xml }
+ format.json { machine_configs.to_json }
+ end
+ end
+ end
+
+ operation :show do
+ control do
+ machine_conf = MachineConfiguration.find(params[:id], self)
+ respond_to do |format|
+ format.xml { machine_conf.to_xml }
+ format.json { machine_conf.to_json }
+ end
+ end
+
+ end
+ end
+
+ end
+end
diff --git a/server/lib/cimi/collections/machine_images.rb b/server/lib/cimi/collections/machine_images.rb
new file mode 100644
index 0000000..1296279
--- /dev/null
+++ b/server/lib/cimi/collections/machine_images.rb
@@ -0,0 +1,50 @@
+# 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 CIMI::Collections
+ class MachineImages < Base
+
+ check_capability :for => lambda { |m| driver.respond_to? m }
+
+ collection :machine_images do
+ description 'List all machine images'
+
+ operation :index do
+ description "List all machine configurations"
+ param :CIMISelect, :string, :optional
+ control do
+ machine_images = MachineImageCollection.default(self).filter_by(params[:CIMISelect])
+ respond_to do |format|
+ format.xml { machine_images.to_xml }
+ format.json { machine_images.to_json }
+ end
+ end
+ end
+
+ operation :show do
+ description "Show specific machine image."
+ control do
+ machine_image = MachineImage.find(params[:id], self)
+ respond_to do |format|
+ format.xml { machine_image.to_xml }
+ format.json { machine_image.to_json }
+ end
+ end
+ end
+
+ end
+
+ end
+end
diff --git a/server/lib/cimi/collections/machines.rb b/server/lib/cimi/collections/machines.rb
new file mode 100644
index 0000000..0abf63a
--- /dev/null
+++ b/server/lib/cimi/collections/machines.rb
@@ -0,0 +1,157 @@
+# 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 CIMI::Collections
+ class Machines < Base
+
+ check_capability :for => lambda { |m| driver.respond_to? m }
+
+ collection :machines do
+ description 'List all machine'
+
+ operation :index do
+ param :CIMISelect, :string, :optional
+ description "List all machines"
+ control do
+ machines = MachineCollection.default(self).filter_by(params[:CIMISelect])
+ respond_to do |format|
+ format.xml { machines.to_xml }
+ format.json { machines.to_json }
+ end
+ end
+ end
+
+ operation :show do
+ description "Show specific machine."
+ control do
+ machine = Machine.find(params[:id], self)
+ respond_to do |format|
+ format.xml { machine.to_xml }
+ format.json { machine.to_json }
+ end
+ end
+ end
+
+ operation :create do
+ description "Create a new Machine entity."
+ control do
+ if request.content_type.end_with?("+json")
+ new_machine = Machine.create_from_json(request.body.read, self)
+ else
+ new_machine = Machine.create_from_xml(request.body.read, self)
+ end
+ status 201 # Created
+ respond_to do |format|
+ format.json { new_machine.to_json }
+ format.xml { new_machine.to_xml }
+ end
+ end
+ end
+
+ operation :destroy do
+ description "Delete a specified machine."
+ param :id, :string, :required
+ control do
+ Machine.delete!(params[:id], self)
+ no_content_with_status(200)
+ end
+ end
+
+ action :stop do
+ description "Stop specific machine."
+ control do
+ machine = Machine.find(params[:id], self)
+ if request.content_type.end_with?("+json")
+ action = Action.from_json(request.body.read)
+ else
+ action = Action.from_xml(request.body.read)
+ end
+ machine.perform(action, self) do |operation|
+ no_content_with_status(202) if operation.success?
+ # Handle errors using operation.failure?
+ end
+ end
+ end
+
+ operation :restart do
+ description "Start specific machine."
+ control do
+ machine = Machine.find(params[:id], self)
+ if request.content_type.end_with?("+json")
+ action = Action.from_json(request.body.read)
+ else
+ action = Action.from_xml(request.body.read)
+ end
+ machine.perform(action, self) do |operation|
+ no_content_with_status(202) if operation.success?
+ # Handle errors using operation.failure?
+ end
+ end
+ end
+
+ operation :start do
+ description "Start specific machine."
+ control do
+ machine = Machine.find(params[:id], self)
+ if request.content_type.end_with?("+json")
+ action = Action.from_json(request.body.read)
+ else
+ action = Action.from_xml(request.body.read)
+ end
+ machine.perform(action, self) do |operation|
+ no_content_with_status(202) if operation.success?
+ # Handle errors using operation.failure?
+ end
+ end
+ end
+
+ #NOTE: The routes for attach/detach used here are NOT as specified by CIMI
+ #will likely move later. CIMI specifies PUT of the whole Machine description
+ #with inclusion/ommission of the volumes you want [att|det]ached
+ action :attach_volume, :http_method => :put do
+ description "Attach CIMI Volume(s) to a machine."
+ control do
+ if request.content_type.end_with?("+json")
+ volumes_to_attach = Volume.find_to_attach_from_json(request.body.read, self)
+ else
+ volumes_to_attach = Volume.find_to_attach_from_xml(request.body.read, self)
+ end
+ machine = Machine.attach_volumes(volumes_to_attach, self)
+ respond_to do |format|
+ format.json{ machine.to_json}
+ format.xml{machine.to_xml}
+ end
+ end
+ end
+
+ action :detach_volume, :http_method => :put do
+ description "Detach CIMI Volume(s) from a machine."
+ control do
+ if request.content_type.end_with?("+json")
+ volumes_to_detach = Volume.find_to_attach_from_json(request.body.read, self)
+ else
+ volumes_to_detach = Volume.find_to_attach_from_xml(request.body.read, self)
+ end
+ machine = Machine.detach_volumes(volumes_to_detach, self)
+ respond_to do |format|
+ format.json{ machine.to_json}
+ format.xml{machine.to_xml}
+ end
+ end
+ end
+ end
+
+ end
+end
diff --git a/server/lib/cimi/collections/network_configurations.rb b/server/lib/cimi/collections/network_configurations.rb
new file mode 100644
index 0000000..02452a8
--- /dev/null
+++ b/server/lib/cimi/collections/network_configurations.rb
@@ -0,0 +1,47 @@
+# 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 CIMI::Collections
+ class NetworkConfigurations < Base
+
+ check_capability :for => lambda { |m| driver.respond_to? m }
+ collection :network_configurations do
+
+ operation :index do
+ description 'List all NetworkConfigurations'
+ param :CIMISelect, :string, :optional
+ control do
+ network_configurations = NetworkConfigurationCollection.default(self).filter_by(params[:CIMISelect])
+ respond_to do |format|
+ format.xml { network_configurations.to_xml }
+ format.json { network_configurations.to_json }
+ end
+ end
+ end
+
+ operation :show do
+ description 'Show a specific NetworkConfiguration'
+ control do
+ network_config = NetworkConfiguration.find(params[:id], self)
+ respond_to do |format|
+ format.xml { network_config.to_xml }
+ format.json { network_config.to_json }
+ end
+ end
+ end
+ end
+
+ end
+end
diff --git a/server/lib/cimi/collections/network_templates.rb b/server/lib/cimi/collections/network_templates.rb
new file mode 100644
index 0000000..0ff82b2
--- /dev/null
+++ b/server/lib/cimi/collections/network_templates.rb
@@ -0,0 +1,48 @@
+# 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 CIMI::Collections
+ class NetworkTemplates < Base
+
+ check_capability :for => lambda { |m| driver.respond_to? m }
+ collection :network_templates do
+
+ operation :index do
+ description 'List all Network Templates in the NetworkTemplateCollection'
+ param :CIMISelect, :string, :optional
+ control do
+ network_templates = NetworkTemplateCollection.default(self).filter_by(params[:CIMISelect])
+ respond_to do |format|
+ format.xml {network_templates.to_xml}
+ format.json {network_templates.to_json}
+ end
+ end
+ end
+
+ operation :show do
+ description 'Show a specific Network Template'
+ control do
+ network_template = NetworkTemplate.find(params[:id], self)
+ respond_to do |format|
+ format.xml {network_template.to_xml}
+ format.json {network_template.to_json}
+ end
+ end
+ end
+
+ end
+
+ end
+end
diff --git a/server/lib/cimi/collections/networks.rb b/server/lib/cimi/collections/networks.rb
new file mode 100644
index 0000000..0aa21e8
--- /dev/null
+++ b/server/lib/cimi/collections/networks.rb
@@ -0,0 +1,125 @@
+# 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 CIMI::Collections
+ class Networks < Base
+
+ check_capability :for => lambda { |m| driver.respond_to? m }
+
+ collection :networks do
+ description 'A Network represents an abstraction of a layer 2 broadcast domain'
+
+ operation :index do
+ description "List all Networks"
+ param :CIMISelect, :string, :optional
+ control do
+ networks = NetworkCollection.default(self).filter_by(params[:CIMISelect])
+ respond_to do |format|
+ format.xml { networks.to_xml }
+ format.json { networks.to_json }
+ end
+ end
+ end
+
+ operation :show do
+ description "Show a specific Network"
+ control do
+ network = Network.find(params[:id], self)
+ respond_to do |format|
+ format.xml { network.to_xml }
+ format.json { network.to_json }
+ end
+ end
+ end
+
+ operation :create do
+ description "Create a new Network"
+ control do
+ if request.content_type.end_with?("json")
+ network = Network.create(request.body.read, self, :json)
+ else
+ network = Network.create(request.body.read, self, :xml)
+ end
+ respond_to do |format|
+ format.xml { network.to_xml}
+ format.json { network.to_json }
+ end
+ end
+ end
+
+ operation :destroy do
+ description "Delete a specified Network"
+ param :id, :string, :required
+ control do
+ Network.delete!(params[:id], self)
+ no_content_with_status(200)
+ end
+ end
+
+ action :start do
+ description "Start specific network."
+ control do
+ network = Network.find(params[:id], self)
+ report_error(404) unless network
+ if request.content_type.end_with?("json")
+ action = Action.from_json(request.body.read)
+ else
+ action = Action.from_xml(request.body.read)
+ end
+ network.perform(action, self) do |operation|
+ no_content_with_status(202) if operation.success?
+ # Handle errors using operation.failure?
+ end
+ end
+ end
+
+ action :stop do
+ description "Stop specific network."
+ control do
+ network = Network.find(params[:id], self)
+ report_error(404) unless network
+ if request.content_type.end_with?("json")
+ action = Action.from_json(request.body.read)
+ else
+ action = Action.from_xml(request.body.read)
+ end
+ network.perform(action, self) do |operation|
+ no_content_with_status(202) if operation.success?
+ # Handle errors using operation.failure?
+ end
+ end
+ end
+
+ action :suspend do
+ description "Suspend specific network."
+ control do
+ network = Network.find(params[:id], self)
+ report_error(404) unless network
+ if request.content_type.end_with?("json")
+ action = Action.from_json(request.body.read)
+ else
+ action = Action.from_xml(request.body.read)
+ end
+ network.perform(action, self) do |operation|
+ no_content_with_status(202) if operation.success?
+ # Handle errors using operation.failure?
+ end
+ end
+ end
+
+ end
+
+ end
+end
diff --git a/server/lib/cimi/collections/routing_group_templates.rb b/server/lib/cimi/collections/routing_group_templates.rb
new file mode 100644
index 0000000..995b1f8
--- /dev/null
+++ b/server/lib/cimi/collections/routing_group_templates.rb
@@ -0,0 +1,47 @@
+# 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 CIMI::Collections
+ class RoutingGroupTemplates < Base
+
+ check_capability :for => lambda { |m| driver.respond_to? m }
+ collection :routing_group_templates do
+
+ operation :index do
+ description 'List all RoutingGroupTemplates in the RoutingGroupTemplateCollection'
+ param :CIMISelect, :string, :optional
+ control do
+ routing_group_templates = RoutingGroupTemplateCollection.default(self).filter_by(params[:CIMISelect])
+ respond_to do |format|
+ format.xml {routing_group_templates.to_xml}
+ format.json {routing_group_templates.to_json}
+ end
+ end
+ end
+
+ operation :show do
+ description 'Show a specific RoutingGroupTemplate'
+ control do
+ routing_group_template = RoutingGroupTemplate.find(params[:id], self)
+ respond_to do |format|
+ format.xml {routing_group_template.to_xml}
+ format.json {routing_group_template.to_json}
+ end
+ end
+ end
+ end
+
+ end
+end
diff --git a/server/lib/cimi/collections/routing_groups.rb b/server/lib/cimi/collections/routing_groups.rb
new file mode 100644
index 0000000..5e7ccc5
--- /dev/null
+++ b/server/lib/cimi/collections/routing_groups.rb
@@ -0,0 +1,48 @@
+# 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 CIMI::Collections
+ class RoutingGroups < Base
+
+ check_capability :for => lambda { |m| driver.respond_to? m }
+ collection :routing_groups do
+
+ operation :index do
+ description 'List all RoutingGroups in the RoutingGroupsCollection'
+ param :CIMISelect, :string, :optional
+ control do
+ routing_groups = RoutingGroupCollection.default(self).filter_by(params[:CIMISelect])
+ respond_to do |format|
+ format.xml {routing_groups.to_xml}
+ format.json {routing_groups.to_json}
+ end
+ end
+ end
+
+ operation :show do
+ description 'Show a specific RoutingGroup'
+ control do
+ routing_group = RoutingGroup.find(params[:id], self)
+ respond_to do |format|
+ format.xml {routing_group.to_xml}
+ format.json {routing_group.to_json}
+ end
+ end
+ end
+
+ end
+
+ end
+end
diff --git a/server/lib/cimi/collections/volume_configurations.rb b/server/lib/cimi/collections/volume_configurations.rb
new file mode 100644
index 0000000..055e28c
--- /dev/null
+++ b/server/lib/cimi/collections/volume_configurations.rb
@@ -0,0 +1,48 @@
+# 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 CIMI::Collections
+ class VolumeConfigurations < Base
+
+ check_capability :for => lambda { |m| driver.respond_to? m }
+
+ collection :volume_configurations do
+
+ operation :index do
+ description "Get list all VolumeConfigurations"
+ param :CIMISelect, :string, :optional
+ control do
+ volume_configuration = VolumeConfigurationCollection.default(self).filter_by(params[:CIMISelect])
+ respond_to do |format|
+ format.xml { volume_configuration.to_xml }
+ format.json { volume_configuration.to_json }
+ end
+ end
+ end
+
+ operation :show do
+ description "Get a specific VolumeConfiguration"
+ control do
+ volume_config = VolumeConfiguration.find(params[:id], self)
+ respond_to do |format|
+ format.xml { volume_config.to_xml }
+ format.json { volume_config.json }
+ end
+ end
+ end
+ end
+
+ end
+end
diff --git a/server/lib/cimi/collections/volume_images.rb b/server/lib/cimi/collections/volume_images.rb
new file mode 100644
index 0000000..78432b1
--- /dev/null
+++ b/server/lib/cimi/collections/volume_images.rb
@@ -0,0 +1,50 @@
+# 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 CIMI::Collections
+ class VolumeImages < Base
+
+ check_capability :for => lambda { |m| driver.respond_to? m }
+
+ collection :volume_images do
+ description 'This entity represents an image that could be place on a pre-loaded volume.'
+
+ operation :index do
+ description "List all volumes images"
+ param :CIMISelect, :string, :optional
+ control do
+ volume_images = VolumeImageCollection.default(self).filter_by(params[:CIMISelect])
+ respond_to do |format|
+ format.xml { volume_images.to_xml }
+ format.json { volume_images.to_json }
+ end
+ end
+ end
+
+ operation :show do
+ description "Show a specific volume image"
+ control do
+ volume_image = VolumeImage.find(params[:id], self)
+ respond_to do |format|
+ format.xml { volume_image.to_xml }
+ format.json { volume_image.to_json }
+ end
+ end
+ end
+ end
+
+
+ end
+end
diff --git a/server/lib/cimi/collections/volumes.rb b/server/lib/cimi/collections/volumes.rb
new file mode 100644
index 0000000..1c45c9e
--- /dev/null
+++ b/server/lib/cimi/collections/volumes.rb
@@ -0,0 +1,80 @@
+# 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 CIMI::Collections
+ class Volumes < Base
+
+ check_capability :for => lambda { |m| driver.respond_to? m }
+ collection :volumes do
+
+ operation :index do
+ description "List all volumes"
+ param :CIMISelect, :string, :optional
+ control do
+ volumes = VolumeCollection.default(self).filter_by(params[:CIMISelect])
+ respond_to do |format|
+ format.xml { volumes.to_xml }
+ format.json { volumes.to_json }
+ end
+ end
+ end
+
+ operation :show do
+ description "Show specific Volume."
+ control do
+ volume = Volume.find(params[:id], self)
+ if volume
+ respond_to do |format|
+ format.xml { volume.to_xml }
+ format.json { volume.to_json }
+ end
+ else
+ report_error(404)
+ end
+ end
+ end
+
+ operation :create do
+ description "Create a new Volume."
+ control do
+ content_type = (request.content_type.end_with?("+json") ? :json : :xml)
+ #((request.content_type.end_with?("+xml")) ? :xml : report_error(415) ) FIXME
+ case content_type
+ when :json
+ new_volume = Volume.create_from_json(request.body.read, self)
+ when :xml
+ new_volume = Volume.create_from_xml(request.body.read, self)
+ end
+ respond_to do |format|
+ format.json { new_volume.to_json }
+ format.xml { new_volume.to_xml }
+ end
+ end
+ end
+
+ operation :destroy do
+ description "Delete a specified Volume"
+ param :id, :string, :required
+ control do
+ Volume.delete!(params[:id], self)
+ no_content_with_status(200)
+ end
+ end
+
+ end
+
+
+ end
+end
diff --git a/server/lib/cimi/collections/vsp_configurations.rb b/server/lib/cimi/collections/vsp_configurations.rb
new file mode 100644
index 0000000..31acceb
--- /dev/null
+++ b/server/lib/cimi/collections/vsp_configurations.rb
@@ -0,0 +1,48 @@
+# 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 CIMI::Collections
+ class VspConfigurations < Base
+
+ check_capability :for => lambda { |m| driver.respond_to? m }
+ collection :vsp_configurations do
+
+ operation :index do
+ description 'List all VSPConfigurations in the VSPConfigurationCollection'
+ param :CIMISelect, :string, :optional
+ control do
+ vsp_configs = VSPConfigurationCollection.default(self).filter_by(params[:CIMISelect])
+ respond_to do |format|
+ format.xml {vsp_configs.to_xml}
+ format.json {vsp_configs.to_json}
+ end
+ end
+ end
+
+ operation :show do
+ description 'Show a specific VSPConfiguration'
+ control do
+ vsp_config = VSPConfiguration.find(params[:id], self)
+ respond_to do |format|
+ format.xml {vsp_config.to_xml}
+ format.json {vsp_config.to_json}
+ end
+ end
+ end
+
+ end
+
+ end
+end
diff --git a/server/lib/cimi/collections/vsp_templates.rb b/server/lib/cimi/collections/vsp_templates.rb
new file mode 100644
index 0000000..503de50
--- /dev/null
+++ b/server/lib/cimi/collections/vsp_templates.rb
@@ -0,0 +1,50 @@
+# 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 CIMI::Collections
+ class VspTemplates < Base
+
+ check_capability :for => lambda { |m| driver.respond_to? m }
+ collection :vsp_templates do
+
+ description 'The VSP Template is a set of Configuration values for realizing a VSP. A VSP Template may be used to create multiple VSPs'
+
+ operation :index do
+ description 'List all VSPTemplates in the VSPTemplateCollection'
+ param :CIMISelect, :string, :optional
+ control do
+ vsp_templates = VSPTemplateCollection.default(self).filter_by(params[:CIMISelect])
+ respond_to do |format|
+ format.xml {vsp_templates.to_xml}
+ format.json {vsp_templates.to_json}
+ end
+ end
+ end
+
+ operation :show do
+ description 'Show a specific VSPTemplate'
+ control do
+ vsp_template = VSPTemplate.find(params[:id], self)
+ respond_to do |format|
+ format.xml {vsp_template.to_xml}
+ format.json {vsp_template.to_json}
+ end
+ end
+ end
+
+ end
+
+ end
+end
diff --git a/server/lib/cimi/collections/vsps.rb b/server/lib/cimi/collections/vsps.rb
new file mode 100644
index 0000000..52b1a5a
--- /dev/null
+++ b/server/lib/cimi/collections/vsps.rb
@@ -0,0 +1,108 @@
+# 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 CIMI::Collections
+ class Vsps < Base
+
+ check_capability :for => lambda { |m| driver.respond_to? m }
+ collection :vsps do
+
+ description 'A VSP represents the connection parameters of a network port'
+
+ operation :index do
+ description 'List all VSPs in the VSPCollection'
+ param :CIMISelect, :string, :optional
+ control do
+ vsps = VSPCollection.default(self).filter_by(params[:CIMISelect])
+ respond_to do |format|
+ format.xml {vsps.to_xml}
+ format.json {vsps.to_json}
+ end
+ end
+ end
+
+ operation :show do
+ description 'Show a specific VSP'
+ control do
+ vsp = VSP.find(params[:id], self)
+ respond_to do |format|
+ format.xml {vsp.to_xml}
+ format.json {vsp.to_json}
+ end
+ end
+ end
+
+ operation :create do
+ description "Create a new VSP"
+ control do
+ if request.content_type.end_with?("json")
+ vsp = CIMI::Model::VSP.create(request.body.read, self, :json)
+ else
+ vsp = CIMI::Model::VSP.create(request.body.read, self, :xml)
+ end
+ respond_to do |format|
+ format.xml { vsp.to_xml }
+ format.json { vsp.to_json }
+ end
+ end
+ end
+
+ operation :destroy do
+ description "Delete a specified VSP"
+ control do
+ CIMI::Model::VSP.delete!(params[:id], self)
+ no_content_with_status(200)
+ end
+ end
+
+ action :start do
+ description "Start specific VSP."
+ param :id, :string, :required
+ control do
+ vsp = VSP.find(params[:id], self)
+ report_error(404) unless vsp
+ if request.content_type.end_with?("json")
+ action = Action.from_json(request.body.read)
+ else
+ action = Action.from_xml(request.body.read)
+ end
+ vsp.perform(action, self) do |operation|
+ no_content_with_status(202) if operation.success?
+ # Handle errors using operation.failure?
+ end
+ end
+ end
+
+ action :stop do
+ description "Stop specific VSP."
+ control do
+ vsp = VSP.find(params[:id], self)
+ report_error(404) unless vsp
+ if request.content_type.end_with?("json")
+ action = Action.from_json(request.body.read)
+ else
+ action = Action.from_xml(request.body.read)
+ end
+ vsp.perform(action, self) do |operation|
+ no_content_with_status(202) if operation.success?
+ # Handle errors using operation.failure?
+ end
+ end
+ end
+
+ end
+
+ end
+end
diff --git a/server/lib/cimi/helpers.rb b/server/lib/cimi/helpers.rb
new file mode 100644
index 0000000..4535c39
--- /dev/null
+++ b/server/lib/cimi/helpers.rb
@@ -0,0 +1,104 @@
+# 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; end
+module CIMI; end
+
+require_relative '../deltacloud/drivers'
+require_relative '../deltacloud/models'
+require_relative '../deltacloud/helpers/driver_helper'
+require_relative '../deltacloud/helpers/auth_helper'
+require_relative '../deltacloud/helpers/url_helper'
+require_relative '../deltacloud/helpers/assets_helper'
+require_relative '../deltacloud/helpers/deltacloud_helper'
+require_relative '../deltacloud/helpers/rabbit_helper'
+require_relative '../deltacloud/helpers/rabbit_helper'
+require_relative '../deltacloud/core_ext/string'
+require_relative '../deltacloud/core_ext/array'
+require_relative '../deltacloud/core_ext/hash'
+require_relative '../deltacloud/core_ext/integer'
+require_relative '../deltacloud/core_ext/proc'
+require_relative './helpers/cimi_helper'
+require_relative './models'
+
+module CIMI::Collections
+ class Base < Sinatra::Base
+
+ extend Deltacloud::Helpers::Drivers
+ include Sinatra::Rabbit::Features
+ include CIMI::Model
+
+ helpers Deltacloud::Helpers::Drivers
+ helpers Sinatra::AuthHelper
+ helpers Sinatra::UrlForHelper
+ helpers Sinatra::StaticAssets::Helpers
+ helpers Rack::RespondTo::Helpers
+ helpers Deltacloud::Helpers::Application
+
+ register Rack::RespondTo
+
+ enable :xhtml
+ enable :dump_errors
+ enable :show_errors
+ disable :show_exceptions
+
+ set :root_url, API_ROOT_URL
+ set :version, API_VERSION
+ set :root, File.join(File.dirname(__FILE__), '..', '..')
+ set :views, root + '/views/cimi'
+ set :public_folder, root + '/public'
+
+ error do
+ report_error
+ end
+
+ error Deltacloud::ExceptionHandler::ValidationFailure do
+ report_error
+ end
+
+ 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
+ end
+
+ after do
+ headers 'X-CIMI-Specification-Version' => API_VERSION
+ end
+
+ def self.new_route_for(route, &block)
+ get route_for('/' + route.to_s + '/new') do
+ instance_eval(&block) if block_given?
+ respond_to do |format|
+ format.html do
+ haml :"#{route}/new"
+ end
+ end
+ end
+ end
+
+ def self.check_capability(opts={})
+ Sinatra::Rabbit.set :check_capability, opts[:for]
+ end
+
+ def self.check_features(opts={})
+ Sinatra::Rabbit.set :check_features, opts[:for]
+ end
+
+ def self.route_for(url)
+ "#{settings.root_url}#{url}"
+ end
+
+ end
+end
diff --git a/server/lib/cimi/model.rb b/server/lib/cimi/model.rb
deleted file mode 100644
index fa3c771..0000000
--- a/server/lib/cimi/model.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-# 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.
-#
-
-# Declare namespace for CIMI model
-#
-module CIMI
- module Model; end
-end
-
-require 'cimi/model/schema'
-require 'cimi/model/base'
-require 'cimi/model/errors'
-require 'cimi/model/cloud_entry_point'
-require 'cimi/model/machine_template'
-require 'cimi/model/machine_image'
-require 'cimi/model/machine_configuration'
-require 'cimi/model/action'
-require 'cimi/model/machine'
-require 'cimi/model/volume'
-require 'cimi/model/machine_admin'
-require 'cimi/model/volume_configuration'
-require 'cimi/model/volume_image'
-require 'cimi/model/volume_template'
-require 'cimi/model/machine_template_collection'
-require 'cimi/model/machine_image_collection'
-require 'cimi/model/machine_configuration_collection'
-require 'cimi/model/machine_collection'
-require 'cimi/model/volume_collection'
-require 'cimi/model/machine_admin_collection'
-require 'cimi/model/volume_configuration_collection'
-require 'cimi/model/volume_image_collection'
-require 'cimi/model/volume_template_collection'
-require 'cimi/model/entity_metadata'
-require 'cimi/model/entity_metadata_collection'
-require 'cimi/model/network'
-require 'cimi/model/network_collection'
-require 'cimi/model/network_configuration'
-require 'cimi/model/network_configuration_collection'
-require 'cimi/model/network_template'
-require 'cimi/model/network_template_collection'
-require 'cimi/model/routing_group'
-require 'cimi/model/routing_group_collection'
-require 'cimi/model/routing_group_template'
-require 'cimi/model/routing_group_template_collection'
-require 'cimi/model/vsp'
-require 'cimi/model/vsp_collection'
-require 'cimi/model/vsp_configuration'
-require 'cimi/model/vsp_configuration_collection'
-require 'cimi/model/vsp_template'
-require 'cimi/model/vsp_template_collection'
-require 'cimi/model/address'
-require 'cimi/model/address_collection'
-require 'cimi/model/address_template'
-require 'cimi/model/address_template_collection'
diff --git a/server/lib/cimi/model/action.rb b/server/lib/cimi/model/action.rb
deleted file mode 100644
index c1c9908..0000000
--- a/server/lib/cimi/model/action.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-# 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.
-
-class CIMI::Model::Action < CIMI::Model::Base
-
- text :action
-
- def name
- action.split('/').last.strip.intern
- end
-
-end
diff --git a/server/lib/cimi/model/address.rb b/server/lib/cimi/model/address.rb
deleted file mode 100644
index 8838331..0000000
--- a/server/lib/cimi/model/address.rb
+++ /dev/null
@@ -1,72 +0,0 @@
-# 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.
-
-class CIMI::Model::Address < CIMI::Model::Base
-
- text :ip
-
- text :hostname
-
- text :allocation
-
- text :default_gateway
-
- text :dns
-
- text :mac_address
-
- text :protocol
-
- text :mask
-
- href :network
-
- href :resource
-
- array :operations do
- scalar :rel, :href
- end
-
- def self.find(id, context)
- if id==:all
- context.driver.addresses(context.credentials, {:env=>context})
- else
- context.driver.addresses(context.credentials, {:id=>id, :env=>context})
- end
- end
-
- def self.create(request_body, context, type)
- input = (type == :xml)? XmlSimple.xml_in(request_body, {"ForceArray"=>false, "NormaliseSpace"=>2}) : JSON.parse(request_body)
- if input["addressTemplate"]["href"] #by reference
- address_template = AddressTemplate.find(context.href_id(input["addressTemplate"]["href"], :address_templates), context)
- else
- case type
- when :json
- address_template = AddressTemplate.from_json(JSON.generate(input["addressTemplate"]))
- when :xml
- xml = XmlSimple.xml_in(request_body, {"NormaliseSpace"=>2})
- address_template = AddressTemplate.from_xml(XmlSimple.xml_out(xml["addressTemplate"][0]))
- end
- end
- params = {:name=>input["name"], :description=>input["description"], :address_template=>address_template, :env=>context }
- raise CIMI::Model::BadRequest.new("Bad request - missing required parameters. Client sent: #{request_body} which produced #{params.inspect}") if params.has_value?(nil)
- context.driver.create_address(context.credentials, params)
- end
-
- def self.delete!(id, context)
- context.driver.delete_address(context.credentials, id)
- end
-
-end
diff --git a/server/lib/cimi/model/address_collection.rb b/server/lib/cimi/model/address_collection.rb
deleted file mode 100644
index eef6c51..0000000
--- a/server/lib/cimi/model/address_collection.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.
-
-class CIMI::Model::AddressCollection < CIMI::Model::Base
-
- act_as_root_entity :address
-
- array :addresses do
- scalar :href
- end
-
- def self.default(context)
- self.new(
- :id => context.addresses_url,
- :name => 'default',
- :created => Time.now,
- :description => "#{context.driver.name.capitalize} AddressCollection",
- :addresses => CIMI::Model::Address.all(context).map { |addr| { :href => addr.id } }
- )
- end
-
-end
diff --git a/server/lib/cimi/model/address_template.rb b/server/lib/cimi/model/address_template.rb
deleted file mode 100644
index 9d2c409..0000000
--- a/server/lib/cimi/model/address_template.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-# 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.
-
-class CIMI::Model::AddressTemplate < CIMI::Model::Base
-
- text :ip
-
- text :hostname
-
- text :allocation
-
- text :default_gateway
-
- text :dns
-
- text :mac_address
-
- text :protocol
-
- text :mask
-
- href :network
-
- array :operations do
- scalar :rel, :href
- end
-
- def self.find(id, context)
- if id==:all
- context.driver.address_templates(context.credentials, {:env=>context})
- else
- context.driver.address_templates(context.credentials, {:id=>id, :env=>context})
- end
- end
-
- def self.create(request_body, context, type)
- end
-
- def self.delete!(id, context)
- end
-
-end
diff --git a/server/lib/cimi/model/address_template_collection.rb b/server/lib/cimi/model/address_template_collection.rb
deleted file mode 100644
index e973252..0000000
--- a/server/lib/cimi/model/address_template_collection.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.
-
-class CIMI::Model::AddressTemplateCollection < CIMI::Model::Base
-
- act_as_root_entity :address_template
-
- array :address_templates do
- scalar :href
- end
-
- def self.default(context)
- self.new(
- :id => context.address_templates_url,
- :name => 'default',
- :created => Time.now,
- :description => "#{context.driver.name.capitalize} AddressTemplateCollection",
- :address_templates => AddressTemplate.all(context).map { |addr| { :href => addr.id } }
- )
- end
-
-end
diff --git a/server/lib/cimi/model/base.rb b/server/lib/cimi/model/base.rb
deleted file mode 100644
index 16812ac..0000000
--- a/server/lib/cimi/model/base.rb
+++ /dev/null
@@ -1,249 +0,0 @@
-# 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.
-
-require 'xmlsimple'
-require 'json'
-
-# The base class for any CIMI object that we either read from a request or
-# write as a response. This class handles serializing/deserializing XML and
-# JSON into a common form.
-#
-# == Defining the schema
-#
-# The conversion of XML and JSON into internal objects is based on a schema
-# that is defined through a DSL:
-#
-# class Machine < CIMI::Model::Base
-# text :status
-# href :meter
-# array :volumes do
-# scalar :href, :attachment_point, :protocol
-# end
-# end
-#
-# The DSL automatically takes care of converting identifiers from their
-# underscored form to the camel-cased form used by CIMI. The above class
-# can be used in the following way:
-#
-# machine = Machine.from_xml(some_xml)
-# if machine.status == "UP"
-# ...
-# end
-# sda = machine.volumes.find { |v| v.attachment_point == "/dev/sda" }
-# handle_meter(machine.meter.href)
-#
-# The keywords for the DSL are
-# [scalar(names, ...)]
-# Define a scalar attribute; in JSON, this is represented as a string
-# property. In XML, this can be represented in a number of ways,
-# depending on whether the option :text is set:
-# * :text not set: attribute on the enclosing element
-# * :text == :direct: the text content of the enclosing element
-# * :text == :nested: the text content of an element +<name>...</name>+
-# [text(names)]
-# A shorthand for +scalar(names, :text => :nested)+, i.e., for
-# attributes that in XML are represented by their own tags
-# [href(name)]
-# A shorthand for +struct name { scalar :href }+; in JSON, this is
-# represented as +{ name: { "href": string } }+, and in XML as +<name
-# href="..."/>+
-# [struct(name, opts, &block)]
-# A structured subobject; the block defines the schema of the
-# subobject. The +:content+ option can be used to specify the attribute
-# that should receive the content of hte corresponding XML element
-# [array(name, opts, &block)]
-# An array of structured subobjects; the block defines the schema of
-# the subobjects.
-
-module CIMI::Model
-
- def self.register_as_root_entity!(name)
- @root_entities ||= []
- @root_entities << name
- unless CIMI::Model::CloudEntryPoint.href_defined?(name)
- CIMI::Model::CloudEntryPoint.send(:href, name.underscore)
- end
- end
-
- def self.root_entities
- @root_entities || []
- end
-
-end
-
-class CIMI::Model::Base
-
- #
- # We keep the values of the attributes in a hash
- #
- attr_reader :attribute_values
-
- # Keep the list of all attributes in an array +attributes+; for each
- # attribute, we also define a getter and a setter to access/change the
- # value for that attribute
- class << self
- def base_schema
- @schema ||= CIMI::Model::Schema.new
- end
-
- def clone_base_schema
- @schema_duped = true
- @schema = Marshal::load(Marshal.dump(superclass.base_schema))
- end
-
- def base_schema_cloned?
- @schema_duped
- end
-
- private :'clone_base_schema', :'base_schema_cloned?'
-
- def inherited(child)
- child.instance_eval do
- def schema
- base_schema_cloned? ? @schema : clone_base_schema
- end
- end
- end
-
- def add_attributes!(names, attr_klass, &block)
- if self.respond_to? :schema
- schema.add_attributes!(names, attr_klass, &block)
- else
- base_schema.add_attributes!(names, attr_klass, &block)
- end
- names.each do |name|
- define_method(name) { @attribute_values[name] }
- define_method(:"#{name}=") { |newval| @attribute_values[name] = newval }
- end
- end
-
- # Return Array of links to current CIMI object
- def all_uri(context)
- self.all(context).map { |e| { :href => e.id } }
- end
- end
-
- extend CIMI::Model::Schema::DSL
-
- def [](a)
- @attribute_values[a]
- end
-
- def []=(a, v)
- @attribute_values[a] = v
- end
-
- #
- # Factory methods
- #
- def initialize(values = {})
- @attribute_values = values
- end
-
- # Construct a new object from the XML representation +xml+
- def self.from_xml(text)
- xml = XmlSimple.xml_in(text, :force_content => true)
- model = self.new
- @schema.from_xml(xml, model)
- model
- end
-
- # Construct a new object
- def self.from_json(text)
- json = JSON::parse(text)
- model = self.new
- @schema.from_json(json, model)
- model
- end
-
- #
- # Serialize
- #
-
- def self.xml_tag_name
- self.name.split("::").last
- end
-
- def self.to_json(model)
- JSON::unparse(@schema.to_json(model))
- end
-
- def self.to_xml(model)
- xml = @schema.to_xml(model)
- xml["xmlns"] = "http://www.dmtf.org/cimi"
- XmlSimple.xml_out(xml, :root_name => xml_tag_name)
- end
-
- def to_json
- self.class.to_json(self)
- end
-
- def to_xml
- self.class.to_xml(self)
- end
-
- #
- # Common attributes for all resources
- #
- text :id, :name, :description, :created
-
- # FIXME: this doesn't match with JSON
- hash :property, :content => :value do
- scalar :name
- end
-
- def self.act_as_root_entity(name=nil)
- if name
- name = name.to_s.camelize.pluralize
- else
- name = xml_tag_name.pluralize.uncapitalize
- end
- CIMI::Model.register_as_root_entity! name
- end
-
- def self.all(_self); find(:all, _self); end
-
- def filter_by(filter_opts)
- return self if filter_opts.nil?
- return filter_attributes(filter_opts.split(',').map{ |a| a.intern }) if filter_opts.include? ','
- case filter_opts
- when /^([\w\_]+)$/ then filter_attributes([$1.intern])
- when /^([\w\_]+)\[(\d+\-\d+)\]$/ then filter_by_arr_range($1.intern, $2)
- when /^([\w\_]+)\[(\d+)\]$/ then filter_by_arr_index($1.intern, $2)
- else self
- end
- end
-
- private
-
- def filter_attributes(attr_list)
- attrs = attr_list.inject({}) do |result, attr|
- result[attr] = self.send(attr) if self.respond_to?(attr)
- result
- end
- self.class.new(attrs)
- end
-
- def filter_by_arr_index(attr, filter)
- return self unless self.respond_to?(attr)
- self.class.new(attr => [self.send(attr)[filter.to_i]])
- end
-
- def filter_by_arr_range(attr, filter)
- return self unless self.respond_to?(attr)
- filter = filter.split('-').inject { |s,e| s.to_i..e.to_i }
- self.class.new(attr => self.send(attr)[filter])
- end
-end
diff --git a/server/lib/cimi/model/cloud_entry_point.rb b/server/lib/cimi/model/cloud_entry_point.rb
deleted file mode 100644
index 5db94df..0000000
--- a/server/lib/cimi/model/cloud_entry_point.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-# 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.
-
-class CIMI::Model::CloudEntryPoint < CIMI::Model::Base
-
- array :entity_metadata do
- scalar :href
- end
-
- def self.create(context)
- self.new(entities(context).merge({
- :name => context.driver.name,
- :description => "Cloud Entry Point for the Deltacloud #{context.driver.name} driver",
- :id => context.cloudEntryPoint_url,
- :created => Time.now,
- :entity_metadata => EntityMetadata.all_uri(context)
- }))
- end
-
- # Return an Hash of the CIMI root entities used in CloudEntryPoint
- def self.entities(context)
- CIMI::Model.root_entities.inject({}) do |result, entity|
- if context.respond_to? :"#{entity.underscore}_url"
- result[entity.underscore] = { :href => context.send(:"#{entity.underscore}_url") }
- end
- result
- end
- end
-
- private
-
- def self.href_defined?(entity)
- true if schema.attribute_names.include? entity.underscore
- end
-
-end
diff --git a/server/lib/cimi/model/entity_metadata.rb b/server/lib/cimi/model/entity_metadata.rb
deleted file mode 100644
index bc20ff3..0000000
--- a/server/lib/cimi/model/entity_metadata.rb
+++ /dev/null
@@ -1,83 +0,0 @@
-# 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.
-
-
-class CIMI::Model::EntityMetadata < CIMI::Model::Base
-
-text :type_uri
-
- array :attributes do
- scalar :name
- scalar :namespace
- scalar :type
- scalar :required
- scalar :constraints
- end
-
- array :operations do
- scalar :name
- scalar :uri
- scalar :description
- scalar :method
- scalar :input_message
- scalar :output_message
- end
-
- def self.find(id, context)
- entity_metadata = []
- if id == :all
- CIMI::Model.root_entities.each do |entity|
- entity_class = CIMI::Model.const_get("#{entity.singularize}")
- entity_metadata << entity_class.create_entity_metadata(context) if entity_class.respond_to?(:create_entity_metadata)
- end
- return entity_metadata
- else
- entity_class = CIMI::Model.const_get("#{id.camelize}")
- if entity_class.respond_to?(:create_entity_metadata)
- entity_class.create_entity_metadata(context)
- end
- end
- end
-
- def self.metadata_from_deltacloud_features(cimi_entity, dcloud_entity, context)
- deltacloud_features = context.driver.features(dcloud_entity)
- metadata_attributes = deltacloud_features.map{|f| attributes_from_feature(f)}
- from_feature(cimi_entity, context, metadata_attributes.flatten!)
- end
-
- def includes_attribute?(attribute)
- self.attributes.any?{|attr| attr[:name] == attribute}
- end
-
- private
-
- def self.attributes_from_feature(feature)
- feature.operations.first.params.inject([]) do |result, param|
- result << {
- :name=>(feature.name == :user_name ? :name : param[0]),
- :type=> "xs:string",
- :required=> (param[1] and param[1].optional?) ? "false" : "true",
- :constraints=> (feature.constraints.empty? ? (feature.description.nil? ? "" : feature.description): feature.constraints)
- }
- end
- end
-
- def self.from_feature(cimi_entity, context, metadata_attributes)
- self.new(:name => cimi_entity, :uri=>"#{context.entity_metadata_url}/#{cimi_entity.underscore}",
- :type_uri=> context.send("#{cimi_entity.pluralize.underscore}_url"),
- :attributes => metadata_attributes)
- end
-
-end
diff --git a/server/lib/cimi/model/entity_metadata_collection.rb b/server/lib/cimi/model/entity_metadata_collection.rb
deleted file mode 100644
index 63db7f7..0000000
--- a/server/lib/cimi/model/entity_metadata_collection.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-# 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.
-
-class CIMI::Model::EntityMetadataCollection < CIMI::Model::Base
-
- array :entity_metadata do
- scalar :href
- end
-
- def self.default(context)
- self.new(
- :id => context.entity_metadata_url,
- :name => 'default',
- :created => Time.now,
- :entity_metadata => EntityMetadata.all_uri(context)
- )
- end
-
-end
diff --git a/server/lib/cimi/model/errors.rb b/server/lib/cimi/model/errors.rb
deleted file mode 100644
index 7c090ed..0000000
--- a/server/lib/cimi/model/errors.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-# 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 CIMI::Model
-
- class NotFound < StandardError
- attr_accessor :code
-
- def initialize
- super("Requested Entity Not Found")
- self.code = 404
- end
-
- end
-
- class BadRequest < StandardError
- attr_accessor :code
- def initialize(msg="")
- super(msg)
- self.code=400
- end
- end
-
- class NotImplemented < StandardError
- attr_accessor :code
-
- def initialize
- super("Requested operation is not implemented by backend provider")
- self.code = 501
- end
-
- end
-
-end
-
-
diff --git a/server/lib/cimi/model/machine.rb b/server/lib/cimi/model/machine.rb
deleted file mode 100644
index 939629a..0000000
--- a/server/lib/cimi/model/machine.rb
+++ /dev/null
@@ -1,229 +0,0 @@
-# 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.
-
-require 'deltacloud/models/instance_address'
-
-class CIMI::Model::Machine < CIMI::Model::Base
-
- text :state
- text :cpu
-
- struct :memory do
- scalar :quantity
- scalar :units
- end
-
- href :event_log
-
- array :disks do
- struct :capacity do
- scalar :quantity
- scalar :units
- end
- scalar :format
- scalar :attachment_point
- end
-
- array :volumes do
- scalar :href
- scalar :protocol
- scalar :attachment_point
- end
-
- array :network_interfaces do
- href :vsp
- text :hostname, :mac_address, :state, :protocol, :allocation
- text :address, :default_gateway, :dns, :max_transmission_unit
- end
-
- array :meters do
- scalar :href
- end
-
- array :operations do
- scalar :rel, :href
- end
-
- def self.find(id, context)
- instances = []
- if id == :all
- instances = context.driver.instances(context.credentials)
- instances.map { |instance| from_instance(instance, context) }.compact
- else
- instance = context.driver.instance(context.credentials, :id => id)
- raise CIMI::Model::NotFound unless instance
- from_instance(instance, context)
- end
- end
-
- def self.create_from_json(body, context)
- json = JSON.parse(body)
- hardware_profile_id = xml['machineTemplate']['machineConfig']["href"].split('/').last
- image_id = xml['machineTemplate']['machineImage']["href"].split('/').last
- instance = context.create_instance(context.credentials, image_id, { :hwp_id => hardware_profile_id })
- from_instance(instance, context)
- end
-
- def self.create_from_xml(body, context)
- xml = XmlSimple.xml_in(body)
- machine_template = xml['machineTemplate'][0]
- hardware_profile_id = machine_template['machineConfig'][0]["href"].split('/').last
- image_id = machine_template['machineImage'][0]["href"].split('/').last
- additional_params = {}
- additional_params[:name] =xml['name'][0] if xml['name']
- if machine_template.has_key? 'machineAdmin'
- additional_params[:keyname] = machine_template['machineAdmin'][0]["href"].split('/').last
- end
- instance = context.driver.create_instance(context.credentials, image_id, {
- :hwp_id => hardware_profile_id
- }.merge(additional_params))
- from_instance(instance, context)
- end
-
- def perform(action, context, &block)
- begin
- if context.driver.send(:"#{action.name}_instance", context.credentials, self.name)
- block.callback :success
- else
- raise "Operation failed to execute on given Machine"
- end
- rescue => e
- block.callback :failure, e.message
- end
- end
-
- def self.delete!(id, context)
- context.driver.destroy_instance(context.credentials, id)
- end
-
- def self.create_entity_metadata(context)
- cimi_entity = self.name.split("::").last
- metadata = EntityMetadata.metadata_from_deltacloud_features(cimi_entity, :instances, context)
- unless metadata.includes_attribute?(:name)
- metadata.attributes << {:name=>"name", :required=>"false",
- :constraints=>"Determined by the cloud provider", :type=>"xs:string"}
- end
- metadata
- end
-
- def self.attach_volumes(volumes, context)
- volumes.each do |vol|
- context.driver.attach_storage_volume(context.credentials,
- {:id=>vol[:volume].name, :instance_id=>context.params[:id], :device=>vol[:attachment_point]})
- end
- self.find(context.params[:id], context)
- end
-
- def self.detach_volumes(volumes, context)
- volumes.each do |vol|
- context.driver.detach_storage_volume(context.credentials, {:id=>vol[:volume].name, :instance_id => context.params[:id]})
- end
- self.find(context.params[:id], context)
- end
-
- private
-
- def self.from_instance(instance, context)
- cpu = memory = disks = (instance.instance_profile.id == "opaque")? "n/a" : nil
- self.new(
- :name => instance.id,
- :description => instance.name,
- :created => instance.launch_time,
- :id => context.machine_url(instance.id),
- :state => convert_instance_state(instance.state),
- :cpu => cpu || convert_instance_cpu(instance.instance_profile, context),
- :memory => memory || convert_instance_memory(instance.instance_profile, context),
- :disks => disks || convert_instance_storage(instance.instance_profile, context),
- :network_interfaces => convert_instance_addresses(instance),
- :operations => convert_instance_actions(instance, context),
- :volumes=>convert_storage_volumes(instance, context),
- :property => convert_instance_properties(instance, context)
- )
- end
-
- # FIXME: This will convert 'RUNNING' state to 'STARTED'
- # which is defined in CIMI (p65)
- #
- def self.convert_instance_state(state)
- ('RUNNING' == state) ? 'STARTED' : state
- end
-
- def self.convert_instance_properties(instance, context)
- properties = []
- properties << { :name => :machine_image, :value => context.machine_image_url(instance.image_id) }
- if instance.respond_to? :keyname
- properties << { :name => :machine_admin, :value => context.machine_admin_url(instance.keyname) }
- end
- properties
- end
-
- def self.convert_instance_cpu(profile, context)
- cpu_override = profile.overrides.find { |p, v| p == :cpu }
- if cpu_override.nil?
- MachineConfiguration.find(profile.id, context).cpu
- else
- cpu_override[1]
- end
- end
-
- def self.convert_instance_memory(profile, context)
- machine_conf = MachineConfiguration.find(profile.name, context)
- memory_override = profile.overrides.find { |p, v| p == :memory }
- {
- :quantity => memory_override.nil? ? machine_conf.memory[:quantity] : memory_override[1],
- :units => machine_conf.memory[:units]
- }
- end
-
- def self.convert_instance_storage(profile, context)
- machine_conf = MachineConfiguration.find(profile.name, context)
- storage_override = profile.overrides.find { |p, v| p == :storage }
- [
- { :capacity => {
- :quantity => storage_override.nil? ? machine_conf.disks.first[:capacity][:quantity] : storage_override[1],
- :units => machine_conf.disks.first[:capacity][:units]
- }
- }
- ]
- end
-
- def self.convert_instance_addresses(instance)
- (instance.public_addresses + instance.private_addresses).collect do |address|
- {
- :hostname => address.is_hostname? ? address : nil,
- :mac_address => address.is_mac? ? address : nil,
- :state => 'Active',
- :protocol => 'IPv4',
- :address => address.is_ipv4? ? address : nil,
- :allocation => 'Static'
- }
- end
- end
-
- def self.convert_instance_actions(instance, context)
- instance.actions.collect do |action|
- action = :destroy if action == :delete # In CIMI destroy operation become delete
- action = :restart if action == :reboot # In CIMI reboot operation become restart
- { :href => context.send(:"#{action}_machine_url", instance.id), :rel => "http://www.dmtf.org/cimi/action/#{action}" }
- end
- end
-
- def self.convert_storage_volumes(instance, context)
- instance.storage_volumes ||= [] #deal with nilpointers
- instance.storage_volumes.map{|vol| {:href=>context.volume_url(vol.keys.first),
- :attachment_point=>vol.values.first} }
- end
-
-end
diff --git a/server/lib/cimi/model/machine_admin.rb b/server/lib/cimi/model/machine_admin.rb
deleted file mode 100644
index fed7c80..0000000
--- a/server/lib/cimi/model/machine_admin.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-# 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.
-
-class CIMI::Model::MachineAdmin < CIMI::Model::Base
-
- text :username
- text :password
- text :key
-
- array :operations do
- scalar :rel, :href
- end
-
- def self.find(id, context)
- if id == :all
- keys = context.driver.keys(context.credentials)
- keys.map { |key| from_key(key, context) }
- else
- key = context.driver.key(context.credentials, :id => id)
- from_key(key, context)
- end
- end
-
- def self.create_from_xml(body, context)
- machine_admin = MachineAdmin.from_xml(body)
- key = context.driver.create_key(context.credentials, :key_name => machine_admin.name)
- from_key(key, context)
- end
-
- def self.delete!(id, context)
- context.driver.destroy_key(context.credentials, :id => id)
- end
-
- private
-
- def self.from_key(key, context)
- self.new(
- :name => key.id,
- :username => key.username,
- :password => key.is_password? ? key.password : key.fingerprint,
- :key => key.is_key? ? key.pem_rsa_key : nil,
- :id => context.machine_admin_url(key.id),
- :created => Time.now
- )
- end
-
-end
diff --git a/server/lib/cimi/model/machine_admin_collection.rb b/server/lib/cimi/model/machine_admin_collection.rb
deleted file mode 100644
index fd76a52..0000000
--- a/server/lib/cimi/model/machine_admin_collection.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.
-
-class CIMI::Model::MachineAdminCollection < CIMI::Model::Base
-
- act_as_root_entity :machine_admin
-
- array :machine_admins do
- scalar :href
- end
-
- def self.default(context)
- self.new(
- :id => context.machine_admins_url,
- :name => 'default',
- :created => Time.now,
- :description => "#{context.driver.name.capitalize} MachineAdminCollection",
- :machine_admins => MachineAdmin.all_uri(context)
- )
- end
-
-end
diff --git a/server/lib/cimi/model/machine_collection.rb b/server/lib/cimi/model/machine_collection.rb
deleted file mode 100644
index 27aa61b..0000000
--- a/server/lib/cimi/model/machine_collection.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.
-
-class CIMI::Model::MachineCollection < CIMI::Model::Base
-
- act_as_root_entity :machine
-
- array :machines do
- scalar :href
- end
-
- def self.default(context)
- self.new(
- :id => context.machines_url,
- :name => 'default',
- :created => Time.now,
- :description => "#{context.driver.name.capitalize} MachineCollection",
- :machines => Machine.all_uri(context)
- )
- end
-
-end
diff --git a/server/lib/cimi/model/machine_configuration.rb b/server/lib/cimi/model/machine_configuration.rb
deleted file mode 100644
index f9d98f2..0000000
--- a/server/lib/cimi/model/machine_configuration.rb
+++ /dev/null
@@ -1,70 +0,0 @@
-# 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.
-
-class CIMI::Model::MachineConfiguration < CIMI::Model::Base
-
- struct :memory do
- scalar :quantity
- scalar :units
- end
-
- text :cpu
-
- array :disks do
- struct :capacity do
- scalar :quantity
- scalar :units
- end
- scalar :format
- scalar :attachment_point
- end
-
- array :operations do
- scalar :rel, :href
- end
-
- def self.find(id, context)
- profiles = []
- if id == :all
- profiles = context.driver.hardware_profiles(context.credentials)
- profiles.map { |profile| from_hardware_profile(profile, context) }.compact
- else
- profile = context.driver.hardware_profile(context.credentials, id)
- from_hardware_profile(profile, context)
- end
- end
-
- private
-
- def self.from_hardware_profile(profile, context)
- # We accept just profiles with all properties set
- return unless profile.memory or profile.cpu or profile.storage
- memory = profile.memory.value || profile.memory.default
- cpu = profile.cpu.value || profile.cpu.default
- storage = profile.storage.value || profile.storage.default
- machine_hash = {
- :name => profile.name,
- :description => "Machine Configuration with #{memory} #{profile.memory.unit} "+
- "of memory and #{cpu} CPU",
- :cpu => cpu,
- :created => Time.now.to_s, # FIXME: DC hardware_profile has no mention about created_at
- :memory => { :quantity => profile.memory.value || profile.memory.default, :units => profile.memory.unit },
- :disks => [ { :capacity => { :quantity => profile.storage.value || profile.storage.default, :units => profile.storage.unit } } ],
- :id => context.machine_configuration_url(profile.name)
- }
- self.new(machine_hash)
- end
-
-end
diff --git a/server/lib/cimi/model/machine_configuration_collection.rb b/server/lib/cimi/model/machine_configuration_collection.rb
deleted file mode 100644
index 171acac..0000000
--- a/server/lib/cimi/model/machine_configuration_collection.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.
-
-class CIMI::Model::MachineConfigurationCollection < CIMI::Model::Base
-
- act_as_root_entity :machine_configuration
-
- array :machine_configurations do
- scalar :href
- end
-
- def self.default(context)
- self.new(
- :id => context.machine_configurations_url,
- :name => 'default',
- :created => Time.now,
- :description => "#{context.driver.name.capitalize} MachineConfigurationCollection",
- :machine_configurations => MachineConfiguration.all_uri(context)
- )
- end
-
-end
diff --git a/server/lib/cimi/model/machine_image.rb b/server/lib/cimi/model/machine_image.rb
deleted file mode 100644
index 7389475..0000000
--- a/server/lib/cimi/model/machine_image.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# 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.
-
-class CIMI::Model::MachineImage < CIMI::Model::Base
-
- href :image_location
- text :image_data
-
- array :operations do
- scalar :rel, :href
- end
-
- def self.find(id, context)
- images = []
- if id == :all
- images = context.driver.images(context.credentials)
- images.map { |image| from_image(image, context) }
- else
- image = context.driver.image(context.credentials, :id => id)
- from_image(image, context)
- end
- end
-
- def self.from_image(image, context)
- self.new(
- :name => image.id,
- :id => context.machine_image_url(image.id),
- :description => image.description,
- :created => Time.now.to_s,
- :image_location => { :href => "#{context.driver.name}://#{image.id}" } # FIXME
- )
- end
-
-end
diff --git a/server/lib/cimi/model/machine_image_collection.rb b/server/lib/cimi/model/machine_image_collection.rb
deleted file mode 100644
index b02ede7..0000000
--- a/server/lib/cimi/model/machine_image_collection.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.
-
-class CIMI::Model::MachineImageCollection < CIMI::Model::Base
-
- act_as_root_entity :machine_image
-
- array :machine_images do
- scalar :href
- end
-
- def self.default(context)
- self.new(
- :id => context.machine_images_url,
- :name => 'default',
- :created => Time.now,
- :description => "#{context.driver.name.capitalize} MachineImageCollection",
- :machine_images => MachineImage.all_uri(context)
- )
- end
-
-end
diff --git a/server/lib/cimi/model/machine_template.rb b/server/lib/cimi/model/machine_template.rb
deleted file mode 100644
index 8a88052..0000000
--- a/server/lib/cimi/model/machine_template.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-# 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.
-
-class CIMI::Model::MachineTemplate < CIMI::Model::Base
-
- href :machine_config
- href :machine_image
- href :machine_admin
-
- array :volumes do
- scalar :href
- scalar :protocol
- scalar :attachment_point
- end
-
- array :volume_templates do
- scalar :href, :attachment_point, :protocol
- end
-
- array :network_interfaces do
- href :vsp
- text :hostname, :mac_address, :state, :protocol, :allocation
- text :address, :default_gateway, :dns, :max_transmission_unit
- end
-
- array :operations do
- scalar :rel, :href
- end
-end
diff --git a/server/lib/cimi/model/machine_template_collection.rb b/server/lib/cimi/model/machine_template_collection.rb
deleted file mode 100644
index e1e8d30..0000000
--- a/server/lib/cimi/model/machine_template_collection.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.
-
-class CIMI::Model::MachineTemplateCollection < CIMI::Model::Base
-
- act_as_root_entity :machine_template
-
- array :machine_templates do
- scalar :href
- end
-
- def self.default(context)
- self.new(
- :id => context.machine_template_url,
- :name => 'default',
- :created => Time.now,
- :description => "#{context.driver.name.capitalize} MachineTemplateCollection",
- :machine_templates => MachineTemplate.all_uri(context)
- )
- end
-
-end
diff --git a/server/lib/cimi/model/network.rb b/server/lib/cimi/model/network.rb
deleted file mode 100644
index cbfbae0..0000000
--- a/server/lib/cimi/model/network.rb
+++ /dev/null
@@ -1,106 +0,0 @@
-# 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.
-
-class CIMI::Model::Network < CIMI::Model::Base
-
- text :state
-
- text :access
-
- text :bandwidth_limit
-
- text :traffic_priority
-
- text :max_traffic_delay
-
- text :max_traffic_loss
-
- text :max_traffic_jitter
-
- href :routing_group
-
- href :event_log
-
- array :meters do
- scalar :href
- end
-
- array :operations do
- scalar :rel, :href
- end
-
- def self.find(id, context)
- networks=[]
- if id==:all
- networks = context.driver.networks(context.credentials, {:env=>context})
- else
- networks = context.driver.networks(context.credentials, {:id=>id, :env=>context})
- end
- networks
- end
-
- def self.create(request_body, context, type)
- input = (type == :xml)? XmlSimple.xml_in(request_body, {"ForceArray"=>false,"NormaliseSpace"=>2}) : JSON.parse(request_body)
- if input["networkTemplate"]["href"] #template by reference
- network_config, routing_group = get_by_reference(input, context)
- else
- if input["networkTemplate"]["networkConfig"]["href"] # configuration by reference
- network_config = NetworkConfiguration.find(context.href_id(input["networkTemplate"]["networkConfig"]["href"],:network_configurations), context)
- else #configuration by value
- network_config = get_by_value(request_body, type)
- end
- routing_group = RoutingGroup.find(context.href_id(input["networkTemplate"]["routingGroup"]["href"], :routing_groups), context)
- end
- params = {:network_config => network_config, :routing_group => routing_group, :name=>input["name"], :description=>input["description"], :env=>context}
- raise CIMI::Model::BadRequest.new("Bad request - missing required parameters. Client sent: #{request_body} which produced #{params.inspect}") if params.has_value?(nil)
- context.driver.create_network(context.credentials, params)
- end
-
- def self.delete!(id, context)
- context.driver.delete_network(context.credentials, id)
- end
-
- def perform(action, context, &block)
- begin
- if context.driver.send(:"#{action.name}_network", context.credentials, self.name)
- block.callback :success
- else
- raise "Operation #{action.name} failed to execute on the Network #{self.name} "
- end
- rescue => e
- block.callback :failure, e.message
- end
- end
-
- private
-
- def self.get_by_reference(input, context)
- network_template = NetworkTemplate.find(context.href_id(input["networkTemplate"]["href"], :network_templates), context)
- network_config = NetworkConfiguration.find(context.href_id(network_template.network_config.href, :network_configurations), context)
- routing_group = RoutingGroup.find(context.href_id(network_template.routing_group.href, :routing_groups), context)
- return network_config, routing_group
- end
-
- def self.get_by_value(request_body, type)
- if type == :xml
- xml_arrays = XmlSimple.xml_in(request_body, {"NormaliseSpace"=>2})
- network_config = NetworkConfiguration.from_xml(XmlSimple.xml_out(xml_arrays["networkTemplate"][0]["networkConfig"][0]))
- else
- json = JSON.parse(request_body)
- network_config = NetworkConfiguration.from_json(JSON.generate(json["networkTemplate"]["networkConfig"]))
- end
- end
-
-end
diff --git a/server/lib/cimi/model/network_collection.rb b/server/lib/cimi/model/network_collection.rb
deleted file mode 100644
index 836d8e8..0000000
--- a/server/lib/cimi/model/network_collection.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.
-
-class CIMI::Model::NetworkCollection < CIMI::Model::Base
-
- act_as_root_entity :network
-
- array :networks do
- scalar :href
- end
-
- def self.default(context)
- self.new(
- :id => context.networks_url,
- :name => 'default',
- :created => Time.now,
- :description => "#{context.driver.name.capitalize} NetworkCollection",
- :networks => Network.all(context).map { |c| { :href => c.id } }
- )
- end
-
-end
diff --git a/server/lib/cimi/model/network_configuration.rb b/server/lib/cimi/model/network_configuration.rb
deleted file mode 100644
index 1b04548..0000000
--- a/server/lib/cimi/model/network_configuration.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-# 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.
-
-class CIMI::Model::NetworkConfiguration < CIMI::Model::Base
-
- text :access
-
- text :bandwidth_limit
-
- text :traffic_priority
-
- text :max_traffic_delay
-
- text :max_traffic_loss
-
- text :max_traffic_jitter
-
- array :operations do
- scalar :rel, :href
- end
-
- def self.find(id, context)
- network_configs = []
- if id==:all
- network_configs = context.driver.network_configurations(context.credentials, {:env=>context})
- else
- network_configs = context.driver.network_configurations(context.credentials, {:env=>context, :id=>id})
- end
- network_configs
- end
-
- def self.create_from_xml(request_body, context)
- end
-
- def self.create_from_json(request_body, context)
- end
-end
diff --git a/server/lib/cimi/model/network_configuration_collection.rb b/server/lib/cimi/model/network_configuration_collection.rb
deleted file mode 100644
index fd70047..0000000
--- a/server/lib/cimi/model/network_configuration_collection.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.
-
-class CIMI::Model::NetworkConfigurationCollection < CIMI::Model::Base
-
- act_as_root_entity :network_configuration
-
- array :network_configurations do
- scalar :href
- end
-
- def self.default(context)
- self.new(
- :id => context.network_configurations_url,
- :name => 'default',
- :created => Time.now,
- :description => "#{context.driver.name.capitalize} NetworkConfigurationCollection",
- :network_configurations => NetworkConfiguration.all(context).map { |c| { :href => c.id } }
- )
- end
-
-end
diff --git a/server/lib/cimi/model/network_template.rb b/server/lib/cimi/model/network_template.rb
deleted file mode 100644
index ce3b990..0000000
--- a/server/lib/cimi/model/network_template.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# 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.
-
-class CIMI::Model::NetworkTemplate < CIMI::Model::Base
-
- href :network_config
-
- href :routing_group
-
- array :operations do
- scalar :rel, :href
- end
-
- def self.find(id, context)
- network_templates = []
- if id==:all
- network_templates = context.driver.network_templates(context.credentials, {:env=>context})
- else
- network_templates = context.driver.network_templates(context.credentials, {:env=>context, :id=>id})
- end
- network_templates
- end
-
-end
diff --git a/server/lib/cimi/model/network_template_collection.rb b/server/lib/cimi/model/network_template_collection.rb
deleted file mode 100644
index 6b97b6e..0000000
--- a/server/lib/cimi/model/network_template_collection.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-# 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.
-
-
-class CIMI::Model::NetworkTemplateCollection < CIMI::Model::Base
-
- act_as_root_entity :network_template
-
- array :network_templates do
- scalar :href
- end
-
- def self.default(context)
- self.new(
- :id => context.network_templates_url,
- :name => 'default',
- :created => Time.now,
- :description => "#{context.driver.name.capitalize} NetworkTemplateCollection",
- :network_templates => NetworkTemplate.all_uri(context)
- )
- end
-
-end
diff --git a/server/lib/cimi/model/routing_group.rb b/server/lib/cimi/model/routing_group.rb
deleted file mode 100644
index d26f4d7..0000000
--- a/server/lib/cimi/model/routing_group.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.
-
-class CIMI::Model::RoutingGroup < CIMI::Model::Base
-
- array :networks do
- scalar :href
- end
-
- array :operations do
- scalar :rel, :href
- end
-
- def self.find(id, context)
- if id==:all
- context.driver.routing_groups(context.credentials, {:env=>context})
- else
- context.driver.routing_groups(context.credentials, {:env=>context, :id=>id})
- end
- end
-
-end
diff --git a/server/lib/cimi/model/routing_group_collection.rb b/server/lib/cimi/model/routing_group_collection.rb
deleted file mode 100644
index 1546c80..0000000
--- a/server/lib/cimi/model/routing_group_collection.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.
-
-class CIMI::Model::RoutingGroupCollection < CIMI::Model::Base
-
- act_as_root_entity :routing_group
-
- array :routing_groups do
- scalar :href
- end
-
- def self.default(context)
- self.new(
- :id => context.routing_groups_url,
- :name => 'default',
- :created => Time.now,
- :description => "#{context.driver.name.capitalize} RoutingGroupCollection",
- :routing_groups => RoutingGroup.all_uri(context)
- )
- end
-
-end
diff --git a/server/lib/cimi/model/routing_group_template.rb b/server/lib/cimi/model/routing_group_template.rb
deleted file mode 100644
index 204a353..0000000
--- a/server/lib/cimi/model/routing_group_template.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.
-
-class CIMI::Model::RoutingGroupTemplate < CIMI::Model::Base
-
- array :networks do
- scalar :href
- end
-
- array :operations do
- scalar :rel, :href
- end
-
- def self.find(id, context)
- if id==:all
- context.driver.routing_group_templates(context.credentials, {:env=>context})
- else
- context.driver.routing_group_templates(context.credentials, {:env=>context, :id=>id})
- end
- end
-
-end
diff --git a/server/lib/cimi/model/routing_group_template_collection.rb b/server/lib/cimi/model/routing_group_template_collection.rb
deleted file mode 100644
index 5e7a9ba..0000000
--- a/server/lib/cimi/model/routing_group_template_collection.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-# 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.
-
-
-class CIMI::Model::RoutingGroupTemplateCollection < CIMI::Model::Base
-
- act_as_root_entity :routing_group_template
-
- array :routing_group_templates do
- scalar :href
- end
-
- def self.default(context)
- self.new(
- :id => context.routing_group_templates_url,
- :name => 'default',
- :created => Time.now,
- :description => "#{context.driver.name.capitalize} RoutingGroupTemplateCollection",
- :routing_group_templates => RoutingGroupTemplate.all_uri(context)
- )
- end
-
-end
diff --git a/server/lib/cimi/model/schema.rb b/server/lib/cimi/model/schema.rb
deleted file mode 100644
index f301b69..0000000
--- a/server/lib/cimi/model/schema.rb
+++ /dev/null
@@ -1,277 +0,0 @@
-# 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.
-#
-
-# The smarts of converting from XML and JSON into internal objects
-class CIMI::Model::Schema
-
- #
- # Attributes describe how we extract values from XML/JSON
- #
- class Attribute
- attr_reader :name, :xml_name, :json_name
-
- def initialize(name, opts = {})
- @name = name
- @xml_name = (opts[:xml_name] || name).to_s.camelize(true)
- @json_name = (opts[:json_name] || name).to_s.camelize(true)
- end
-
- def from_xml(xml, model)
- model[@name] = xml[@xml_name].first if xml.has_key?(@xml_name)
- end
-
- def from_json(json, model)
- model[@name] = json[@json_name]
- end
-
- def to_xml(model, xml)
- xml[@xml_name] = [model[@name]] if model[@name]
- end
-
- def to_json(model, json)
- json[@json_name] = model[@name] if model and model[@name]
- end
- end
-
- class Scalar < Attribute
- def initialize(name, opts)
- @text = opts[:text]
- if ! [nil, :nested, :direct].include?(@text)
- raise "text option for scalar must be :nested or :direct"
- end
- super(name, opts)
- end
-
- def text?; @text; end
-
- def nested_text?; @text == :nested; end
-
- def from_xml(xml, model)
- case @text
- when :nested then model[@name] = xml[@xml_name].first["content"] if xml[@xml_name]
- when :direct then model[@name] = xml["content"]
- else model[@name] = xml[@xml_name]
- end
- end
-
- def to_xml(model, xml)
- return unless model
- return unless model[@name]
- case @text
- when :nested then xml[@xml_name] = [{ "content" => model[@name] }]
- when :direct then xml["content"] = model[@name]
- else xml[@xml_name] = model[@name]
- end
- end
- end
-
- class Struct < Attribute
- def initialize(name, opts, &block)
- content = opts[:content]
- super(name, opts)
- @schema = CIMI::Model::Schema.new
- @schema.instance_eval(&block) if block_given?
- @schema.scalar(content, :text => :direct) if content
- end
-
- def from_xml(xml, model)
- xml = xml.has_key?(xml_name) ? xml[xml_name].first : {}
- model[name] = convert_from_xml(xml)
- end
-
- def from_json(json, model)
- json = json.has_key?(json_name) ? json[json_name] : {}
- model[name] = convert_from_json(json)
- end
-
- def to_xml(model, xml)
- conv = convert_to_xml(model[name])
- xml[xml_name] = [conv] unless conv.empty?
- end
-
- def to_json(model, json)
- conv = convert_to_json(model[name])
- json[json_name] = conv unless conv.empty?
- end
-
- def convert_from_xml(xml)
- sub = struct.new
- @schema.from_xml(xml, sub)
- sub
- end
-
- def convert_from_json(json)
- sub = struct.new
- @schema.from_json(json, sub)
- sub
- end
-
- def convert_to_xml(model)
- xml = {}
- @schema.to_xml(model, xml)
- xml
- end
-
- def convert_to_json(model)
- json = {}
- @schema.to_json(model, json)
- json
- end
-
- private
- def struct
- cname = "CIMI_#{json_name.upcase_first}"
- if ::Struct.const_defined?(cname)
- ::Struct.const_get(cname)
- else
- ::Struct.new("CIMI_#{json_name.upcase_first}",
- *@schema.attribute_names)
- end
- end
- end
-
- class Array < Attribute
- # For an array :things, we collect all <thing/> elements (XmlSimple
- # actually does the collecting)
- def initialize(name, opts = {}, &block)
- opts[:xml_name] = name.to_s.singularize unless opts[:xml_name]
- super(name, opts)
- @struct = Struct.new(name, opts, &block)
- end
-
- def from_xml(xml, model)
- model[name] = (xml[xml_name] || []).map { |elt| @struct.convert_from_xml(elt) }
- end
-
- def from_json(json, model)
- model[name] = (json[json_name] || []).map { |elt| @struct.convert_from_json(elt) }
- end
-
- def to_xml(model, xml)
- ary = (model[name] || []).map { |elt| @struct.convert_to_xml(elt) }
- xml[xml_name] = ary unless ary.empty?
- end
-
- def to_json(model, json)
- ary = (model[name] || []).map { |elt| @struct.convert_to_json(elt) }
- json[json_name] = ary unless ary.empty?
- end
- end
-
- class Hash < Attribute
-
- def initialize(name, opts = {}, &block)
- opts[:json_name] = name.to_s.pluralize unless opts[:json_name]
- super(name, opts)
- @struct = Struct.new(name, opts, &block)
- end
-
- def from_xml(xml, model)
- model[name] = (xml[xml_name] || []).map { |elt| @struct.convert_from_xml(elt) }
- end
-
- def from_json(json, model)
- model[name] = (json[json_name] || {}).inject([]) do |result,item|
- result << @struct.convert_from_json({ 'name' => item[0], 'value' => item[1] })
- end
- end
-
- def to_xml(model, xml)
- ary = (model[name] || []).map { |elt| @struct.convert_to_xml(elt) }
- xml[xml_name] = ary unless ary.empty?
- end
-
- def to_json(model, json)
- ary = (model[name] || []).map { |elt| @struct.convert_to_json(elt) }
- json[json_name] = ary.inject({}) { |result, item| result[item['name']] = item['value']; result } unless ary.empty?
- end
- end
-
- #
- # The actual Schema class
- #
- def initialize
- @attributes = []
- end
-
- def from_xml(xml, model = {})
- @attributes.freeze
- @attributes.each { |attr| attr.from_xml(xml, model) }
- model
- end
-
- def from_json(json, model = {})
- @attributes.freeze
- @attributes.each { |attr| attr.from_json(json, model) }
- model
- end
-
- def to_xml(model, xml = {})
- @attributes.freeze
- @attributes.each { |attr| attr.to_xml(model, xml) }
- xml
- end
-
- def to_json(model, json = {})
- @attributes.freeze
- @attributes.each { |attr| attr.to_json(model, json) }
- json
- end
-
- def attribute_names
- @attributes.map { |a| a.name }
- end
-
- #
- # The DSL
- #
- # Requires that the class into which this is included has a
- # +add_attributes!+ method
- module DSL
- def href(*args)
- args.each { |arg| struct(arg) { scalar :href } }
- end
-
- def text(*args)
- args.expand_opts!(:text => :nested)
- scalar(*args)
- end
-
- def scalar(*args)
- add_attributes!(args, Scalar)
- end
-
- def array(name, opts={}, &block)
- add_attributes!([name, opts], Array, &block)
- end
-
- def struct(name, opts={}, &block)
- add_attributes!([name, opts], Struct, &block)
- end
-
- def hash(name, opts={}, &block)
- add_attributes!([name, opts], Hash, &block)
- end
- end
-
- include DSL
-
- def add_attributes!(args, attr_klass, &block)
- raise "The schema has already been used to convert objects" if @attributes.frozen?
- opts = args.extract_opts!
- args.each { |arg| @attributes << attr_klass.new(arg, opts, &block) }
- end
-end
diff --git a/server/lib/cimi/model/volume.rb b/server/lib/cimi/model/volume.rb
deleted file mode 100644
index 0a232d5..0000000
--- a/server/lib/cimi/model/volume.rb
+++ /dev/null
@@ -1,103 +0,0 @@
-# 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.
-
-class CIMI::Model::Volume < CIMI::Model::Base
-
- struct :capacity do
- scalar :quantity
- scalar :units
- end
- text :bootable
- text :supports_snapshots
- array :snapshots do
- scalar :ref
- end
- text :guest_interface
- array :meters do
- scalar :ref
- end
- href :eventlog
- array :operations do
- scalar :rel, :href
- end
-
- def self.find(id, context)
- volumes = []
- opts = ( id == :all ) ? {} : { :id => id }
- volumes = self.driver.storage_volumes(context.credentials, opts)
- volumes.collect!{ |volume| from_storage_volume(volume, context) }
- return volumes.first unless volumes.length > 1
- return volumes
- end
-
- def self.all(context); find(:all, context); end
-
- def self.create_from_json(json_in, context)
- json = JSON.parse(json_in)
- volume_config_id = json["volumeTemplate"]["volumeConfig"]["href"].split("/").last
- volume_image_id = (json["volumeTemplate"].has_key?("volumeImage") ?
- json["volumeTemplate"]["volumeImage"]["href"].split("/").last : nil)
- create_volume({:volume_config_id=>volume_config_id, :volume_image_id=>volume_image_id}, context)
- end
-
- def self.create_from_xml(xml_in, context)
- xml = XmlSimple.xml_in(xml_in)
- volume_config_id = xml["volumeTemplate"][0]["volumeConfig"][0]["href"].split("/").last
- volume_image_id = (xml["volumeTemplate"][0].has_key?("volumeImage") ?
- xml["volumeTemplate"][0]["volumeImage"][0]["href"].split("/").last : nil)
- create_volume({:volume_config_id=>volume_config_id, :volume_image_id=>volume_image_id}, context)
- end
-
- def self.delete!(id, context)
- context.driver.destroy_storage_volume(context.credentials, {:id=>id} )
- end
-
- def self.find_to_attach_from_json(json_in, context)
- json = JSON.parse(json_in)
- volumes = json["volumes"].map{|v| {:volume=>self.find(v["volume"]["href"].split("/volumes/").last, context),
- :attachment_point=>v["attachmentPoint"] }}
- end
-
- def self.find_to_attach_from_xml(xml_in, context)
- xml = XmlSimple.xml_in(xml_in)
- volumes = xml["volume"].map{|v| {:volume => self.find(v["href"].split("/volumes/").last, context),
- :attachment_point=>v["attachmentPoint"] }}
- end
-
- private
-
- def self.create_volume(params, context)
- volume_config = VolumeConfiguration.find(params[:volume_config_id], context)
- opts = {:capacity=>volume_config.capacity[:quantity], :snapshot_id=>params[:volume_image_id] }
- storage_volume = self.driver.create_storage_volume(context.credentials, opts)
- from_storage_volume(storage_volume, context)
- end
-
- def self.from_storage_volume(volume, context)
- self.new( { :name => volume.id,
- :description => volume.id,
- :created => volume.created,
- :id => context.volume_url(volume.id),
- :capacity => { :quantity=>volume.capacity, :units=>"gibibyte" }, #FIXME... units will vary
- :bootable => "false", #fixme ... will vary... ec2 doesn't expose this
- :supports_snapshots => "true", #fixme, will vary (true for ec2)
- :snapshots => [], #fixme...
- :guest_interface => "",
- :eventlog => {:href=> "http://eventlogs"},#FIXME
- :meters => []
- } )
- end
-
-end
diff --git a/server/lib/cimi/model/volume_collection.rb b/server/lib/cimi/model/volume_collection.rb
deleted file mode 100644
index d5053c7..0000000
--- a/server/lib/cimi/model/volume_collection.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.
-
-class CIMI::Model::VolumeCollection < CIMI::Model::Base
-
- act_as_root_entity :volume
-
- array :volumes do
- scalar :href
- end
-
- def self.default(context)
- self.new(
- :id => context.volumes_url,
- :name => 'default',
- :created => Time.now,
- :description => "#{context.driver.name.capitalize} VolumeCollection",
- :volumes => Volume.all_uri(context)
- )
- end
-
-end
diff --git a/server/lib/cimi/model/volume_configuration.rb b/server/lib/cimi/model/volume_configuration.rb
deleted file mode 100644
index 75b37ea..0000000
--- a/server/lib/cimi/model/volume_configuration.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-# 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.
-
-class CIMI::Model::VolumeConfiguration < CIMI::Model::Base
-
- text :format
- struct :capacity do
- scalar :quantity
- scalar :units
- end
- text :supports_snapshots
- text :guest_interface
- array :operations do
- scalar :rel, :href
- end
-
- def self.find(id, context)
- volume_configs = []
- if id == :all
- #ec2 ebs volumes can 1gb..1tb
- (1..1000).each do |size|
- volume_configs << create(size, context)
- end
- else
- volume_configs << create(id, context)
- return volume_configs.first
- end
- return volume_configs
- end
-
-
- def self.all(context); find(:all, context); end
-
- private
-
- def self.create(size, context)
- self.new( {
- :id => context.volume_configuration_url(size),
- :name => size,
- :description => "volume configuration with #{size} GiB",
- :created => Time.now.to_s,
- :capacity => {:quantity=>size, :units=>"gibibytes"},
- :supports_snapshots => "true"
- # FIXME :guest_interface => "NFS"
- } )
- end
-
-end
diff --git a/server/lib/cimi/model/volume_configuration_collection.rb b/server/lib/cimi/model/volume_configuration_collection.rb
deleted file mode 100644
index ace31bf..0000000
--- a/server/lib/cimi/model/volume_configuration_collection.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.
-
-class CIMI::Model::VolumeConfigurationCollection < CIMI::Model::Base
-
- act_as_root_entity :volume_configuration
-
- array :volume_configurations do
- scalar :href
- end
-
- def self.default(context)
- self.new(
- :id => context.volume_configurations_url,
- :name => 'default',
- :created => Time.now,
- :description => "#{context.driver.name.capitalize} VolumeConfigurationCollection",
- :volume_configurations => VolumeConfiguration.all_uri(context)
- )
- end
-
-end
diff --git a/server/lib/cimi/model/volume_image.rb b/server/lib/cimi/model/volume_image.rb
deleted file mode 100644
index 03cc7fd..0000000
--- a/server/lib/cimi/model/volume_image.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-# 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.
-
-class CIMI::Model::VolumeImage < CIMI::Model::Base
-
- href :image_location
- text :image_data
- text :bootable
- array :operations do
- scalar :rel, :href
- end
-
- def self.find(id, context)
- storage_snapshots = []
- opts = ( id==:all ) ? {} : { :id=>id }
- storage_snapshots = self.driver.storage_snapshots(context.credentials, opts)
- storage_snapshots.collect!{ |snapshot| from_storage_snapshot(snapshot, context) }
- return storage_snapshots.first unless storage_snapshots.length > 1
- return storage_snapshots
- end
-
- def self.all(context); find(:all, context); end
-
- private
-
- def self.from_storage_snapshot(snapshot, context)
- self.new( {
- :name => snapshot.id,
- :description => snapshot.id,
- :created => snapshot.created,
- :id => context.volume_image_url(snapshot.id),
- :image_location => {:href=>context.volume_url(snapshot.storage_volume_id)},
- :bootable => "false" #FIXME
- } )
- end
-
-end
diff --git a/server/lib/cimi/model/volume_image_collection.rb b/server/lib/cimi/model/volume_image_collection.rb
deleted file mode 100644
index 0172c04..0000000
--- a/server/lib/cimi/model/volume_image_collection.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.
-
-class CIMI::Model::VolumeImageCollection < CIMI::Model::Base
-
- act_as_root_entity :volume_image
-
- array :volume_images do
- scalar :href
- end
-
- def self.default(context)
- self.new(
- :id => context.volume_images_url,
- :name => 'default',
- :created => Time.now,
- :description => "#{context.driver.name.capitalize} VolumeImageCollection",
- :volume_images => VolumeImage.all_uri(context)
- )
- end
-
-end
diff --git a/server/lib/cimi/model/volume_template.rb b/server/lib/cimi/model/volume_template.rb
deleted file mode 100644
index b9c82db..0000000
--- a/server/lib/cimi/model/volume_template.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# 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.
-
-class CIMI::Model::VolumeTemplate < CIMI::Model::Base
-
- href :volume_config
- href :volume_image
- array :operations do
- scalar :rel, :href
- end
-end
diff --git a/server/lib/cimi/model/volume_template_collection.rb b/server/lib/cimi/model/volume_template_collection.rb
deleted file mode 100644
index 6b6e4ac..0000000
--- a/server/lib/cimi/model/volume_template_collection.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.
-
-class CIMI::Model::VolumeTemplateCollection < CIMI::Model::Base
-
- act_as_root_entity :volume_template
-
- array :volume_templates do
- scalar :href
- end
-
- def self.default(context)
- self.new(
- :id => context.volume_template_url,
- :name => 'default',
- :created => Time.now,
- :description => "#{context.driver.name.capitalize} VolumeTemplateCollection",
- :volume_templates => VolumeTemplate.all_uri(context)
- )
- end
-
-end
diff --git a/server/lib/cimi/model/vsp.rb b/server/lib/cimi/model/vsp.rb
deleted file mode 100644
index 40a526f..0000000
--- a/server/lib/cimi/model/vsp.rb
+++ /dev/null
@@ -1,102 +0,0 @@
-# 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.
-
-class CIMI::Model::VSP < CIMI::Model::Base
-
- text :state
-
- href :network
-
- text :bandwidth_reservation
-
- text :traffic_priority
-
- text :max_traffic_delay
-
- text :max_traffic_loss
-
- text :max_traffic_jitter
-
- href :event_log
-
- array :meters do
- scalar :href
- end
-
- array :operations do
- scalar :rel, :href
- end
-
- def self.find(id, context)
- if id==:all
- context.driver.vsps(context.credentials, {:env=>context})
- else
- context.driver.vsps(context.credentials, {:id=>id, :env=>context})
- end
- end
-
- def self.create(request_body, context, type)
- input = (type == :xml)? XmlSimple.xml_in(request_body, {"ForceArray"=>false, "NormaliseSpace"=>2}) : JSON.parse(request_body)
- if input["vspTemplate"]["href"] #template by reference
- vsp_config, network = get_by_reference(input, context)
- else
- if input["vspTemplate"]["vspConfig"]["href"] # configuration by reference
- vsp_config = VSPConfiguration.find(context.href_id(input["vspTemplate"]["vspConfig"]["href"],:vsp_configurations), context)
- else #configuration by value
- vsp_config = get_by_value(request_body, type)
- end
- network = Network.find(context.href_id(input["vspTemplate"]["network"]["href"], :networks), context)
- end
- params = {:vsp_config => vsp_config, :network => network, :name=>input["name"], :description=>input["description"], :env=>context}
- raise CIMI::Model::BadRequest.new("Bad request - missing required parameters. Client sent: #{request_body} which produced #{params.inspect}") if params.has_value?(nil)
- context.driver.create_vsp(context.credentials, params)
- end
-
- def self.delete!(id, context)
- context.driver.delete_vsp(context.credentials, id)
- end
-
- def perform(action, context, &block)
- begin
- if context.driver.send(:"#{action.name}_vsp", context.credentials, self.name)
- block.callback :success
- else
- raise "Operation #{action.name} failed to execute on the VSP #{self.name} "
- end
- rescue => e
- block.callback :failure, e.message
- end
- end
-
-
- private
-
- def self.get_by_reference(input, context)
- vsp_template = VSPTemplate.find(context.href_id(input["vspTemplate"]["href"], :vsp_templates), context)
- vsp_config = VSPConfiguration.find(context.href_id(vsp_template.vsp_config.href, :vsp_configurations), context)
- network = Network.find(context.href_id(vsp_template.network.href, :networks), context)
- return vsp_config, network
- end
-
- def self.get_by_value(request_body, type)
- if type == :xml
- xml_arrays = XmlSimple.xml_in(request_body, {"NormaliseSpace"=>2})
- vsp_config = VSPConfiguration.from_xml(XmlSimple.xml_out(xml_arrays["vspTemplate"][0]["vspConfig"][0]))
- else
- json = JSON.parse(request_body)
- vsp_config = VSPConfiguration.from_json(JSON.generate(json["vspTemplate"]["vspConfig"]))
- end
- end
-end
diff --git a/server/lib/cimi/model/vsp_collection.rb b/server/lib/cimi/model/vsp_collection.rb
deleted file mode 100644
index 6f659e1..0000000
--- a/server/lib/cimi/model/vsp_collection.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.
-
-class CIMI::Model::VSPCollection < CIMI::Model::Base
-
- CIMI::Model.register_as_root_entity! "VSPs"
-
- array :vsps do
- scalar :href
- end
-
- def self.default(context)
- self.new(
- :id => context.vsps_url,
- :name => 'default',
- :created => Time.now,
- :description => "#{context.driver.name.capitalize} VSPCollection",
- :vsps => VSP.all_uri(context)
- )
- end
-
-end
diff --git a/server/lib/cimi/model/vsp_configuration.rb b/server/lib/cimi/model/vsp_configuration.rb
deleted file mode 100644
index c9a9bf3..0000000
--- a/server/lib/cimi/model/vsp_configuration.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-# 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.
-
-class CIMI::Model::VSPConfiguration < CIMI::Model::Base
-
- text :bandwidth_reservation
-
- text :traffic_priority
-
- text :max_traffic_delay
-
- text :max_traffic_loss
-
- text :max_traffic_jitter
-
- array :operations do
- scalar :rel, :href
- end
-
- def self.find(id, context)
- if id==:all
- context.driver.vsp_configurations(context.credentials, {:env=>context})
- else
- context.driver.vsp_configurations(context.credentials, {:env=>context, :id=>id})
- end
- end
-
-end
diff --git a/server/lib/cimi/model/vsp_configuration_collection.rb b/server/lib/cimi/model/vsp_configuration_collection.rb
deleted file mode 100644
index addff1c..0000000
--- a/server/lib/cimi/model/vsp_configuration_collection.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.
-
-class CIMI::Model::VSPConfigurationCollection < CIMI::Model::Base
-
- CIMI::Model.register_as_root_entity! "VSPConfigurations"
-
- array :vsp_configurations do
- scalar :href
- end
-
- def self.default(context)
- self.new(
- :id => context.vsp_configurations_url,
- :name => 'default',
- :created => Time.now,
- :description => "#{context.driver.name.capitalize} VSPConfigurationCollection",
- :vsp_configurations => VSPConfiguration.all_uri(context)
- )
- end
-
-end
diff --git a/server/lib/cimi/model/vsp_template.rb b/server/lib/cimi/model/vsp_template.rb
deleted file mode 100644
index f1b8078..0000000
--- a/server/lib/cimi/model/vsp_template.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.
-
-class CIMI::Model::VSPTemplate < CIMI::Model::Base
-
- href :network
-
- href :vsp_config
-
- array :operations do
- scalar :rel, :href
- end
-
- def self.find(id, context)
- if id==:all
- context.driver.vsp_templates(context.credentials, {:env=>context})
- else
- context.driver.vsp_templates(context.credentials, {:env=>context, :id=>id})
- end
- end
-
-end
diff --git a/server/lib/cimi/model/vsp_template_collection.rb b/server/lib/cimi/model/vsp_template_collection.rb
deleted file mode 100644
index 4acb74e..0000000
--- a/server/lib/cimi/model/vsp_template_collection.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.
-
-class CIMI::Model::VSPTemplateCollection < CIMI::Model::Base
-
- CIMI::Model.register_as_root_entity! "VSPTemplates"
-
- array :vsp_templates do
- scalar :href
- end
-
- def self.default(context)
- self.new(
- :id => context.vsp_templates_url,
- :name => 'default',
- :created => Time.now,
- :description => "#{context.driver.name.capitalize} VSPTemplateCollection",
- :vsp_templates => VSPTemplate.all_uri(context)
- )
- end
-
-end
diff --git a/server/lib/cimi/models.rb b/server/lib/cimi/models.rb
new file mode 100644
index 0000000..b0793ee
--- /dev/null
+++ b/server/lib/cimi/models.rb
@@ -0,0 +1,75 @@
+# 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 require_relatived 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.
+#
+
+require_relative '../deltacloud/drivers/features'
+
+# Declare namespace for CIMI models
+#
+
+module CIMI
+ module Model; end
+
+ class FakeCollection
+ extend Sinatra::Rabbit::Features
+ include Deltacloud::InstanceFeatures
+ end
+end
+
+require_relative './models/schema'
+require_relative './models/base'
+require_relative './models/errors'
+require_relative './models/entity_metadata'
+require_relative './models/entity_metadata_collection'
+require_relative './models/cloud_entry_point'
+require_relative './models/machine_template'
+require_relative './models/machine_image'
+require_relative './models/machine_configuration'
+require_relative './models/action'
+require_relative './models/machine'
+require_relative './models/volume'
+require_relative './models/machine_admin'
+require_relative './models/volume_configuration'
+require_relative './models/volume_image'
+require_relative './models/volume_template'
+require_relative './models/machine_template_collection'
+require_relative './models/machine_image_collection'
+require_relative './models/machine_configuration_collection'
+require_relative './models/machine_collection'
+require_relative './models/volume_collection'
+require_relative './models/machine_admin_collection'
+require_relative './models/volume_configuration_collection'
+require_relative './models/volume_image_collection'
+require_relative './models/volume_template_collection'
+require_relative './models/network'
+require_relative './models/network_collection'
+require_relative './models/network_configuration'
+require_relative './models/network_configuration_collection'
+require_relative './models/network_template'
+require_relative './models/network_template_collection'
+require_relative './models/routing_group'
+require_relative './models/routing_group_collection'
+require_relative './models/routing_group_template'
+require_relative './models/routing_group_template_collection'
+require_relative './models/vsp'
+require_relative './models/vsp_collection'
+require_relative './models/vsp_configuration'
+require_relative './models/vsp_configuration_collection'
+require_relative './models/vsp_template'
+require_relative './models/vsp_template_collection'
+require_relative './models/address'
+require_relative './models/address_collection'
+require_relative './models/address_template'
+require_relative './models/address_template_collection'
diff --git a/server/lib/cimi/models/action.rb b/server/lib/cimi/models/action.rb
new file mode 100644
index 0000000..c1c9908
--- /dev/null
+++ b/server/lib/cimi/models/action.rb
@@ -0,0 +1,24 @@
+# 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.
+
+class CIMI::Model::Action < CIMI::Model::Base
+
+ text :action
+
+ def name
+ action.split('/').last.strip.intern
+ end
+
+end
diff --git a/server/lib/cimi/models/address.rb b/server/lib/cimi/models/address.rb
new file mode 100644
index 0000000..f9e10a1
--- /dev/null
+++ b/server/lib/cimi/models/address.rb
@@ -0,0 +1,72 @@
+# 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.
+
+class CIMI::Model::Address < CIMI::Model::Base
+
+ text :ip
+
+ text :hostname
+
+ text :allocation
+
+ text :default_gateway
+
+ text :dns
+
+ text :mac_address
+
+ text :protocol
+
+ text :mask
+
+ href :network
+
+ href :resource
+
+ array :operations do
+ scalar :rel, :href
+ end
+
+ def self.find(id, context)
+ if id==:all
+ context.driver.addresses(context.credentials, {:env=>context})
+ else
+ context.driver.addresses(context.credentials, {:id=>id, :env=>context})
+ end
+ end
+
+ def self.create(request_body, context, type)
+ input = (type == :xml)? XmlSimple.xml_in(request_body, {"ForceArray"=>false, "NormaliseSpace"=>2}) : JSON.parse(request_body)
+ if input["addressTemplate"]["href"] #by reference
+ address_template = CIMI::Model::AddressTemplate.find(context.href_id(input["addressTemplate"]["href"], :address_templates), context)
+ else
+ case type
+ when :json
+ address_template = CIMI::Model::AddressTemplate.from_json(JSON.generate(input["addressTemplate"]))
+ when :xml
+ xml = XmlSimple.xml_in(request_body, {"NormaliseSpace"=>2})
+ address_template = CIMI::Model::AddressTemplate.from_xml(XmlSimple.xml_out(xml["addressTemplate"][0]))
+ end
+ end
+ params = {:name=>input["name"], :description=>input["description"], :address_template=>address_template, :env=>context }
+ raise CIMI::Model::BadRequest.new("Bad request - missing required parameters. Client sent: #{request_body} which produced #{params.inspect}") if params.has_value?(nil)
+ context.driver.create_address(context.credentials, params)
+ end
+
+ def self.delete!(id, context)
+ context.driver.delete_address(context.credentials, id)
+ end
+
+end
diff --git a/server/lib/cimi/models/address_collection.rb b/server/lib/cimi/models/address_collection.rb
new file mode 100644
index 0000000..eef6c51
--- /dev/null
+++ b/server/lib/cimi/models/address_collection.rb
@@ -0,0 +1,34 @@
+# 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.
+
+class CIMI::Model::AddressCollection < CIMI::Model::Base
+
+ act_as_root_entity :address
+
+ array :addresses do
+ scalar :href
+ end
+
+ def self.default(context)
+ self.new(
+ :id => context.addresses_url,
+ :name => 'default',
+ :created => Time.now,
+ :description => "#{context.driver.name.capitalize} AddressCollection",
+ :addresses => CIMI::Model::Address.all(context).map { |addr| { :href => addr.id } }
+ )
+ end
+
+end
diff --git a/server/lib/cimi/models/address_template.rb b/server/lib/cimi/models/address_template.rb
new file mode 100644
index 0000000..9d2c409
--- /dev/null
+++ b/server/lib/cimi/models/address_template.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.
+
+class CIMI::Model::AddressTemplate < CIMI::Model::Base
+
+ text :ip
+
+ text :hostname
+
+ text :allocation
+
+ text :default_gateway
+
+ text :dns
+
+ text :mac_address
+
+ text :protocol
+
+ text :mask
+
+ href :network
+
+ array :operations do
+ scalar :rel, :href
+ end
+
+ def self.find(id, context)
+ if id==:all
+ context.driver.address_templates(context.credentials, {:env=>context})
+ else
+ context.driver.address_templates(context.credentials, {:id=>id, :env=>context})
+ end
+ end
+
+ def self.create(request_body, context, type)
+ end
+
+ def self.delete!(id, context)
+ end
+
+end
diff --git a/server/lib/cimi/models/address_template_collection.rb b/server/lib/cimi/models/address_template_collection.rb
new file mode 100644
index 0000000..3bb95cd
--- /dev/null
+++ b/server/lib/cimi/models/address_template_collection.rb
@@ -0,0 +1,34 @@
+# 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.
+
+class CIMI::Model::AddressTemplateCollection < CIMI::Model::Base
+
+ act_as_root_entity :address_template
+
+ array :address_templates do
+ scalar :href
+ end
+
+ def self.default(context)
+ self.new(
+ :id => context.address_templates_url,
+ :name => 'default',
+ :created => Time.now,
+ :description => "#{context.driver.name.capitalize} AddressTemplateCollection",
+ :address_templates => CIMI::Model::AddressTemplate.all(context).map { |addr| { :href => addr.id } }
+ )
+ end
+
+end
diff --git a/server/lib/cimi/models/base.rb b/server/lib/cimi/models/base.rb
new file mode 100644
index 0000000..16812ac
--- /dev/null
+++ b/server/lib/cimi/models/base.rb
@@ -0,0 +1,249 @@
+# 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.
+
+require 'xmlsimple'
+require 'json'
+
+# The base class for any CIMI object that we either read from a request or
+# write as a response. This class handles serializing/deserializing XML and
+# JSON into a common form.
+#
+# == Defining the schema
+#
+# The conversion of XML and JSON into internal objects is based on a schema
+# that is defined through a DSL:
+#
+# class Machine < CIMI::Model::Base
+# text :status
+# href :meter
+# array :volumes do
+# scalar :href, :attachment_point, :protocol
+# end
+# end
+#
+# The DSL automatically takes care of converting identifiers from their
+# underscored form to the camel-cased form used by CIMI. The above class
+# can be used in the following way:
+#
+# machine = Machine.from_xml(some_xml)
+# if machine.status == "UP"
+# ...
+# end
+# sda = machine.volumes.find { |v| v.attachment_point == "/dev/sda" }
+# handle_meter(machine.meter.href)
+#
+# The keywords for the DSL are
+# [scalar(names, ...)]
+# Define a scalar attribute; in JSON, this is represented as a string
+# property. In XML, this can be represented in a number of ways,
+# depending on whether the option :text is set:
+# * :text not set: attribute on the enclosing element
+# * :text == :direct: the text content of the enclosing element
+# * :text == :nested: the text content of an element +<name>...</name>+
+# [text(names)]
+# A shorthand for +scalar(names, :text => :nested)+, i.e., for
+# attributes that in XML are represented by their own tags
+# [href(name)]
+# A shorthand for +struct name { scalar :href }+; in JSON, this is
+# represented as +{ name: { "href": string } }+, and in XML as +<name
+# href="..."/>+
+# [struct(name, opts, &block)]
+# A structured subobject; the block defines the schema of the
+# subobject. The +:content+ option can be used to specify the attribute
+# that should receive the content of hte corresponding XML element
+# [array(name, opts, &block)]
+# An array of structured subobjects; the block defines the schema of
+# the subobjects.
+
+module CIMI::Model
+
+ def self.register_as_root_entity!(name)
+ @root_entities ||= []
+ @root_entities << name
+ unless CIMI::Model::CloudEntryPoint.href_defined?(name)
+ CIMI::Model::CloudEntryPoint.send(:href, name.underscore)
+ end
+ end
+
+ def self.root_entities
+ @root_entities || []
+ end
+
+end
+
+class CIMI::Model::Base
+
+ #
+ # We keep the values of the attributes in a hash
+ #
+ attr_reader :attribute_values
+
+ # Keep the list of all attributes in an array +attributes+; for each
+ # attribute, we also define a getter and a setter to access/change the
+ # value for that attribute
+ class << self
+ def base_schema
+ @schema ||= CIMI::Model::Schema.new
+ end
+
+ def clone_base_schema
+ @schema_duped = true
+ @schema = Marshal::load(Marshal.dump(superclass.base_schema))
+ end
+
+ def base_schema_cloned?
+ @schema_duped
+ end
+
+ private :'clone_base_schema', :'base_schema_cloned?'
+
+ def inherited(child)
+ child.instance_eval do
+ def schema
+ base_schema_cloned? ? @schema : clone_base_schema
+ end
+ end
+ end
+
+ def add_attributes!(names, attr_klass, &block)
+ if self.respond_to? :schema
+ schema.add_attributes!(names, attr_klass, &block)
+ else
+ base_schema.add_attributes!(names, attr_klass, &block)
+ end
+ names.each do |name|
+ define_method(name) { @attribute_values[name] }
+ define_method(:"#{name}=") { |newval| @attribute_values[name] = newval }
+ end
+ end
+
+ # Return Array of links to current CIMI object
+ def all_uri(context)
+ self.all(context).map { |e| { :href => e.id } }
+ end
+ end
+
+ extend CIMI::Model::Schema::DSL
+
+ def [](a)
+ @attribute_values[a]
+ end
+
+ def []=(a, v)
+ @attribute_values[a] = v
+ end
+
+ #
+ # Factory methods
+ #
+ def initialize(values = {})
+ @attribute_values = values
+ end
+
+ # Construct a new object from the XML representation +xml+
+ def self.from_xml(text)
+ xml = XmlSimple.xml_in(text, :force_content => true)
+ model = self.new
+ @schema.from_xml(xml, model)
+ model
+ end
+
+ # Construct a new object
+ def self.from_json(text)
+ json = JSON::parse(text)
+ model = self.new
+ @schema.from_json(json, model)
+ model
+ end
+
+ #
+ # Serialize
+ #
+
+ def self.xml_tag_name
+ self.name.split("::").last
+ end
+
+ def self.to_json(model)
+ JSON::unparse(@schema.to_json(model))
+ end
+
+ def self.to_xml(model)
+ xml = @schema.to_xml(model)
+ xml["xmlns"] = "http://www.dmtf.org/cimi"
+ XmlSimple.xml_out(xml, :root_name => xml_tag_name)
+ end
+
+ def to_json
+ self.class.to_json(self)
+ end
+
+ def to_xml
+ self.class.to_xml(self)
+ end
+
+ #
+ # Common attributes for all resources
+ #
+ text :id, :name, :description, :created
+
+ # FIXME: this doesn't match with JSON
+ hash :property, :content => :value do
+ scalar :name
+ end
+
+ def self.act_as_root_entity(name=nil)
+ if name
+ name = name.to_s.camelize.pluralize
+ else
+ name = xml_tag_name.pluralize.uncapitalize
+ end
+ CIMI::Model.register_as_root_entity! name
+ end
+
+ def self.all(_self); find(:all, _self); end
+
+ def filter_by(filter_opts)
+ return self if filter_opts.nil?
+ return filter_attributes(filter_opts.split(',').map{ |a| a.intern }) if filter_opts.include? ','
+ case filter_opts
+ when /^([\w\_]+)$/ then filter_attributes([$1.intern])
+ when /^([\w\_]+)\[(\d+\-\d+)\]$/ then filter_by_arr_range($1.intern, $2)
+ when /^([\w\_]+)\[(\d+)\]$/ then filter_by_arr_index($1.intern, $2)
+ else self
+ end
+ end
+
+ private
+
+ def filter_attributes(attr_list)
+ attrs = attr_list.inject({}) do |result, attr|
+ result[attr] = self.send(attr) if self.respond_to?(attr)
+ result
+ end
+ self.class.new(attrs)
+ end
+
+ def filter_by_arr_index(attr, filter)
+ return self unless self.respond_to?(attr)
+ self.class.new(attr => [self.send(attr)[filter.to_i]])
+ end
+
+ def filter_by_arr_range(attr, filter)
+ return self unless self.respond_to?(attr)
+ filter = filter.split('-').inject { |s,e| s.to_i..e.to_i }
+ self.class.new(attr => self.send(attr)[filter])
+ end
+end
diff --git a/server/lib/cimi/models/cloud_entry_point.rb b/server/lib/cimi/models/cloud_entry_point.rb
new file mode 100644
index 0000000..ffe1ef5
--- /dev/null
+++ b/server/lib/cimi/models/cloud_entry_point.rb
@@ -0,0 +1,46 @@
+# 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.
+
+class CIMI::Model::CloudEntryPoint < CIMI::Model::Base
+
+ array :entity_metadata do
+ scalar :href
+ end
+
+ def self.create(context)
+ self.new(entities(context).merge({
+ :name => context.driver.name,
+ :description => "Cloud Entry Point for the Deltacloud #{context.driver.name} driver",
+ :id => context.cloudEntryPoint_url,
+ :created => Time.now,
+ :entity_metadata => CIMI::Model::EntityMetadata.all_uri(context)
+ }))
+ end
+
+ # Return an Hash of the CIMI root entities used in CloudEntryPoint
+ def self.entities(context)
+ CIMI::Model.root_entities.inject({}) do |result, entity|
+ result[entity.underscore] = { :href => context.send(:"#{entity.underscore}_url") }
+ result
+ end
+ end
+
+ private
+
+ def self.href_defined?(entity)
+ true if schema.attribute_names.include? entity.underscore
+ end
+
+end
diff --git a/server/lib/cimi/models/entity_metadata.rb b/server/lib/cimi/models/entity_metadata.rb
new file mode 100644
index 0000000..5455c75
--- /dev/null
+++ b/server/lib/cimi/models/entity_metadata.rb
@@ -0,0 +1,84 @@
+# 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.
+
+
+class CIMI::Model::EntityMetadata < CIMI::Model::Base
+
+ text :type_uri
+
+ array :attributes do
+ scalar :name
+ scalar :namespace
+ scalar :type
+ scalar :required
+ scalar :constraints
+ end
+
+ array :operations do
+ scalar :name
+ scalar :uri
+ scalar :description
+ scalar :method
+ scalar :input_message
+ scalar :output_message
+ end
+
+ def self.find(id, context)
+ entity_metadata = []
+ if id == :all
+ CIMI::Model.root_entities.each do |entity|
+ entity_class = CIMI::Model.const_get("#{entity.singularize}")
+ entity_metadata << entity_class.create_entity_metadata(context) if entity_class.respond_to?(:create_entity_metadata)
+ end
+ return entity_metadata
+ else
+ entity_class = CIMI::Model.const_get("#{id.camelize}")
+ if entity_class.respond_to?(:create_entity_metadata)
+ entity_class.create_entity_metadata(context)
+ end
+ end
+ end
+
+ def self.metadata_from_deltacloud_features(cimi_entity, dcloud_entity, context)
+ deltacloud_features = context.driver.class.features_for(dcloud_entity)
+ metadata_attributes = deltacloud_features.map{|f| attributes_from_feature(f)}
+ from_feature(cimi_entity, context, metadata_attributes.flatten!)
+ end
+
+ def includes_attribute?(attribute)
+ self.attributes.any?{|attr| attr[:name] == attribute}
+ end
+
+ private
+
+ def self.attributes_from_feature(feature)
+ feature = CIMI::FakeCollection.feature(feature)
+ feature.operations.first.params_array.map do |p|
+ {
+ :name=> p.name,
+ :type=> "xs:string",
+ :required=> p.required? ? "true" : "false",
+ :constraints=> (feature.constraints.empty? ? (feature.description.nil? ? "" : feature.description): feature.constraints)
+ }
+ end
+ end
+
+ def self.from_feature(cimi_entity, context, metadata_attributes)
+ self.new(:name => cimi_entity, :uri=>"#{context.entity_metadata_url}/#{cimi_entity.underscore}",
+ :type_uri=> context.send("#{cimi_entity.pluralize.underscore}_url"),
+ :attributes => metadata_attributes)
+ end
+
+end
diff --git a/server/lib/cimi/models/entity_metadata_collection.rb b/server/lib/cimi/models/entity_metadata_collection.rb
new file mode 100644
index 0000000..595b502
--- /dev/null
+++ b/server/lib/cimi/models/entity_metadata_collection.rb
@@ -0,0 +1,31 @@
+# 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.
+
+class CIMI::Model::EntityMetadataCollection < CIMI::Model::Base
+
+ array :entity_metadata do
+ scalar :href
+ end
+
+ def self.default(context)
+ self.new(
+ :id => context.entity_metadata_url,
+ :name => 'default',
+ :created => Time.now,
+ :entity_metadata => CIMI::Model::EntityMetadata.all_uri(context)
+ )
+ end
+
+end
diff --git a/server/lib/cimi/models/errors.rb b/server/lib/cimi/models/errors.rb
new file mode 100644
index 0000000..7c090ed
--- /dev/null
+++ b/server/lib/cimi/models/errors.rb
@@ -0,0 +1,48 @@
+# 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 CIMI::Model
+
+ class NotFound < StandardError
+ attr_accessor :code
+
+ def initialize
+ super("Requested Entity Not Found")
+ self.code = 404
+ end
+
+ end
+
+ class BadRequest < StandardError
+ attr_accessor :code
+ def initialize(msg="")
+ super(msg)
+ self.code=400
+ end
+ end
+
+ class NotImplemented < StandardError
+ attr_accessor :code
+
+ def initialize
+ super("Requested operation is not implemented by backend provider")
+ self.code = 501
+ end
+
+ end
+
+end
+
+
diff --git a/server/lib/cimi/models/machine.rb b/server/lib/cimi/models/machine.rb
new file mode 100644
index 0000000..0540bd8
--- /dev/null
+++ b/server/lib/cimi/models/machine.rb
@@ -0,0 +1,227 @@
+# 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.
+
+class CIMI::Model::Machine < CIMI::Model::Base
+
+ text :state
+ text :cpu
+
+ struct :memory do
+ scalar :quantity
+ scalar :units
+ end
+
+ href :event_log
+
+ array :disks do
+ struct :capacity do
+ scalar :quantity
+ scalar :units
+ end
+ scalar :format
+ scalar :attachment_point
+ end
+
+ array :volumes do
+ scalar :href
+ scalar :protocol
+ scalar :attachment_point
+ end
+
+ array :network_interfaces do
+ href :vsp
+ text :hostname, :mac_address, :state, :protocol, :allocation
+ text :address, :default_gateway, :dns, :max_transmission_unit
+ end
+
+ array :meters do
+ scalar :href
+ end
+
+ array :operations do
+ scalar :rel, :href
+ end
+
+ def self.find(id, context)
+ instances = []
+ if id == :all
+ instances = context.driver.instances(context.credentials)
+ instances.map { |instance| from_instance(instance, context) }.compact
+ else
+ instance = context.driver.instance(context.credentials, :id => id)
+ raise CIMI::Model::NotFound unless instance
+ from_instance(instance, context)
+ end
+ end
+
+ def self.create_from_json(body, context)
+ json = JSON.parse(body)
+ hardware_profile_id = xml['machineTemplate']['machineConfig']["href"].split('/').last
+ image_id = xml['machineTemplate']['machineImage']["href"].split('/').last
+ instance = context.create_instance(context.credentials, image_id, { :hwp_id => hardware_profile_id })
+ from_instance(instance, context)
+ end
+
+ def self.create_from_xml(body, context)
+ xml = XmlSimple.xml_in(body)
+ machine_template = xml['machineTemplate'][0]
+ hardware_profile_id = machine_template['machineConfig'][0]["href"].split('/').last
+ image_id = machine_template['machineImage'][0]["href"].split('/').last
+ additional_params = {}
+ additional_params[:name] =xml['name'][0] if xml['name']
+ if machine_template.has_key? 'machineAdmin'
+ additional_params[:keyname] = machine_template['machineAdmin'][0]["href"].split('/').last
+ end
+ instance = context.driver.create_instance(context.credentials, image_id, {
+ :hwp_id => hardware_profile_id
+ }.merge(additional_params))
+ from_instance(instance, context)
+ end
+
+ def perform(action, context, &block)
+ begin
+ if context.driver.send(:"#{action.name}_instance", context.credentials, self.name)
+ block.callback :success
+ else
+ raise "Operation failed to execute on given Machine"
+ end
+ rescue => e
+ block.callback :failure, e.message
+ end
+ end
+
+ def self.delete!(id, context)
+ context.driver.destroy_instance(context.credentials, id)
+ end
+
+ def self.create_entity_metadata(context)
+ cimi_entity = self.name.split("::").last
+ metadata = CIMI::Model::EntityMetadata.metadata_from_deltacloud_features(cimi_entity, :instances, context)
+ unless metadata.includes_attribute?(:name)
+ metadata.attributes << {:name=>"name", :required=>"false",
+ :constraints=>"Determined by the cloud provider", :type=>"xs:string"}
+ end
+ metadata
+ end
+
+ def self.attach_volumes(volumes, context)
+ volumes.each do |vol|
+ context.driver.attach_storage_volume(context.credentials,
+ {:id=>vol[:volume].name, :instance_id=>context.params[:id], :device=>vol[:attachment_point]})
+ end
+ self.find(context.params[:id], context)
+ end
+
+ def self.detach_volumes(volumes, context)
+ volumes.each do |vol|
+ context.driver.detach_storage_volume(context.credentials, {:id=>vol[:volume].name, :instance_id => context.params[:id]})
+ end
+ self.find(context.params[:id], context)
+ end
+
+ private
+
+ def self.from_instance(instance, context)
+ cpu = memory = disks = (instance.instance_profile.id == "opaque")? "n/a" : nil
+ self.new(
+ :name => instance.id,
+ :description => instance.name,
+ :created => instance.launch_time,
+ :id => context.machine_url(instance.id),
+ :state => convert_instance_state(instance.state),
+ :cpu => cpu || convert_instance_cpu(instance.instance_profile, context),
+ :memory => memory || convert_instance_memory(instance.instance_profile, context),
+ :disks => disks || convert_instance_storage(instance.instance_profile, context),
+ :network_interfaces => convert_instance_addresses(instance),
+ :operations => convert_instance_actions(instance, context),
+ :volumes=>convert_storage_volumes(instance, context),
+ :property => convert_instance_properties(instance, context)
+ )
+ end
+
+ # FIXME: This will convert 'RUNNING' state to 'STARTED'
+ # which is defined in CIMI (p65)
+ #
+ def self.convert_instance_state(state)
+ ('RUNNING' == state) ? 'STARTED' : state
+ end
+
+ def self.convert_instance_properties(instance, context)
+ properties = []
+ properties << { :name => :machine_image, :value => context.machine_image_url(instance.image_id) }
+ if instance.respond_to? :keyname
+ properties << { :name => :machine_admin, :value => context.machine_admin_url(instance.keyname) }
+ end
+ properties
+ end
+
+ def self.convert_instance_cpu(profile, context)
+ cpu_override = profile.overrides.find { |p, v| p == :cpu }
+ if cpu_override.nil?
+ CIMI::Model::MachineConfiguration.find(profile.id, context).cpu
+ else
+ cpu_override[1]
+ end
+ end
+
+ def self.convert_instance_memory(profile, context)
+ machine_conf = CIMI::Model::MachineConfiguration.find(profile.name, context)
+ memory_override = profile.overrides.find { |p, v| p == :memory }
+ {
+ :quantity => memory_override.nil? ? machine_conf.memory[:quantity] : memory_override[1],
+ :units => machine_conf.memory[:units]
+ }
+ end
+
+ def self.convert_instance_storage(profile, context)
+ machine_conf = CIMI::Model::MachineConfiguration.find(profile.name, context)
+ storage_override = profile.overrides.find { |p, v| p == :storage }
+ [
+ { :capacity => {
+ :quantity => storage_override.nil? ? machine_conf.disks.first[:capacity][:quantity] : storage_override[1],
+ :units => machine_conf.disks.first[:capacity][:units]
+ }
+ }
+ ]
+ end
+
+ def self.convert_instance_addresses(instance)
+ (instance.public_addresses + instance.private_addresses).collect do |address|
+ {
+ :hostname => address.is_hostname? ? address : nil,
+ :mac_address => address.is_mac? ? address : nil,
+ :state => 'Active',
+ :protocol => 'IPv4',
+ :address => address.is_ipv4? ? address : nil,
+ :allocation => 'Static'
+ }
+ end
+ end
+
+ def self.convert_instance_actions(instance, context)
+ instance.actions.collect do |action|
+ action = :destroy if action == :delete # In CIMI destroy operation become delete
+ action = :restart if action == :reboot # In CIMI reboot operation become restart
+ { :href => context.send(:"#{action}_machine_url", instance.id), :rel => "http://www.dmtf.org/cimi/action/#{action}" }
+ end
+ end
+
+ def self.convert_storage_volumes(instance, context)
+ instance.storage_volumes ||= [] #deal with nilpointers
+ instance.storage_volumes.map{|vol| {:href=>context.volume_url(vol.keys.first),
+ :attachment_point=>vol.values.first} }
+ end
+
+end
diff --git a/server/lib/cimi/models/machine_admin.rb b/server/lib/cimi/models/machine_admin.rb
new file mode 100644
index 0000000..fed7c80
--- /dev/null
+++ b/server/lib/cimi/models/machine_admin.rb
@@ -0,0 +1,59 @@
+# 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.
+
+class CIMI::Model::MachineAdmin < CIMI::Model::Base
+
+ text :username
+ text :password
+ text :key
+
+ array :operations do
+ scalar :rel, :href
+ end
+
+ def self.find(id, context)
+ if id == :all
+ keys = context.driver.keys(context.credentials)
+ keys.map { |key| from_key(key, context) }
+ else
+ key = context.driver.key(context.credentials, :id => id)
+ from_key(key, context)
+ end
+ end
+
+ def self.create_from_xml(body, context)
+ machine_admin = MachineAdmin.from_xml(body)
+ key = context.driver.create_key(context.credentials, :key_name => machine_admin.name)
+ from_key(key, context)
+ end
+
+ def self.delete!(id, context)
+ context.driver.destroy_key(context.credentials, :id => id)
+ end
+
+ private
+
+ def self.from_key(key, context)
+ self.new(
+ :name => key.id,
+ :username => key.username,
+ :password => key.is_password? ? key.password : key.fingerprint,
+ :key => key.is_key? ? key.pem_rsa_key : nil,
+ :id => context.machine_admin_url(key.id),
+ :created => Time.now
+ )
+ end
+
+end
diff --git a/server/lib/cimi/models/machine_admin_collection.rb b/server/lib/cimi/models/machine_admin_collection.rb
new file mode 100644
index 0000000..08c1559
--- /dev/null
+++ b/server/lib/cimi/models/machine_admin_collection.rb
@@ -0,0 +1,34 @@
+# 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.
+
+class CIMI::Model::MachineAdminCollection < CIMI::Model::Base
+
+ act_as_root_entity :machine_admin
+
+ array :machine_admins do
+ scalar :href
+ end
+
+ def self.default(context)
+ self.new(
+ :id => context.machine_admins_url,
+ :name => 'default',
+ :created => Time.now,
+ :description => "#{context.driver.name.capitalize} MachineAdminCollection",
+ :machine_admins => CIMI::Model::MachineAdmin.all_uri(context)
+ )
+ end
+
+end
diff --git a/server/lib/cimi/models/machine_collection.rb b/server/lib/cimi/models/machine_collection.rb
new file mode 100644
index 0000000..9154aa9
--- /dev/null
+++ b/server/lib/cimi/models/machine_collection.rb
@@ -0,0 +1,34 @@
+# 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.
+
+class CIMI::Model::MachineCollection < CIMI::Model::Base
+
+ act_as_root_entity :machine
+
+ array :machines do
+ scalar :href
+ end
+
+ def self.default(context)
+ self.new(
+ :id => context.machines_url,
+ :name => 'default',
+ :created => Time.now,
+ :description => "#{context.driver.name.capitalize} MachineCollection",
+ :machines => CIMI::Model::Machine.all_uri(context)
+ )
+ end
+
+end
diff --git a/server/lib/cimi/models/machine_configuration.rb b/server/lib/cimi/models/machine_configuration.rb
new file mode 100644
index 0000000..f9d98f2
--- /dev/null
+++ b/server/lib/cimi/models/machine_configuration.rb
@@ -0,0 +1,70 @@
+# 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.
+
+class CIMI::Model::MachineConfiguration < CIMI::Model::Base
+
+ struct :memory do
+ scalar :quantity
+ scalar :units
+ end
+
+ text :cpu
+
+ array :disks do
+ struct :capacity do
+ scalar :quantity
+ scalar :units
+ end
+ scalar :format
+ scalar :attachment_point
+ end
+
+ array :operations do
+ scalar :rel, :href
+ end
+
+ def self.find(id, context)
+ profiles = []
+ if id == :all
+ profiles = context.driver.hardware_profiles(context.credentials)
+ profiles.map { |profile| from_hardware_profile(profile, context) }.compact
+ else
+ profile = context.driver.hardware_profile(context.credentials, id)
+ from_hardware_profile(profile, context)
+ end
+ end
+
+ private
+
+ def self.from_hardware_profile(profile, context)
+ # We accept just profiles with all properties set
+ return unless profile.memory or profile.cpu or profile.storage
+ memory = profile.memory.value || profile.memory.default
+ cpu = profile.cpu.value || profile.cpu.default
+ storage = profile.storage.value || profile.storage.default
+ machine_hash = {
+ :name => profile.name,
+ :description => "Machine Configuration with #{memory} #{profile.memory.unit} "+
+ "of memory and #{cpu} CPU",
+ :cpu => cpu,
+ :created => Time.now.to_s, # FIXME: DC hardware_profile has no mention about created_at
+ :memory => { :quantity => profile.memory.value || profile.memory.default, :units => profile.memory.unit },
+ :disks => [ { :capacity => { :quantity => profile.storage.value || profile.storage.default, :units => profile.storage.unit } } ],
+ :id => context.machine_configuration_url(profile.name)
+ }
+ self.new(machine_hash)
+ end
+
+end
diff --git a/server/lib/cimi/models/machine_configuration_collection.rb b/server/lib/cimi/models/machine_configuration_collection.rb
new file mode 100644
index 0000000..6d7ebad
--- /dev/null
+++ b/server/lib/cimi/models/machine_configuration_collection.rb
@@ -0,0 +1,34 @@
+# 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.
+
+class CIMI::Model::MachineConfigurationCollection < CIMI::Model::Base
+
+ act_as_root_entity :machine_configuration
+
+ array :machine_configurations do
+ scalar :href
+ end
+
+ def self.default(context)
+ self.new(
+ :id => context.machine_configurations_url,
+ :name => 'default',
+ :created => Time.now,
+ :description => "#{context.driver.name.capitalize} MachineConfigurationCollection",
+ :machine_configurations => CIMI::Model::MachineConfiguration.all_uri(context)
+ )
+ end
+
+end
diff --git a/server/lib/cimi/models/machine_image.rb b/server/lib/cimi/models/machine_image.rb
new file mode 100644
index 0000000..7389475
--- /dev/null
+++ b/server/lib/cimi/models/machine_image.rb
@@ -0,0 +1,46 @@
+# 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.
+
+class CIMI::Model::MachineImage < CIMI::Model::Base
+
+ href :image_location
+ text :image_data
+
+ array :operations do
+ scalar :rel, :href
+ end
+
+ def self.find(id, context)
+ images = []
+ if id == :all
+ images = context.driver.images(context.credentials)
+ images.map { |image| from_image(image, context) }
+ else
+ image = context.driver.image(context.credentials, :id => id)
+ from_image(image, context)
+ end
+ end
+
+ def self.from_image(image, context)
+ self.new(
+ :name => image.id,
+ :id => context.machine_image_url(image.id),
+ :description => image.description,
+ :created => Time.now.to_s,
+ :image_location => { :href => "#{context.driver.name}://#{image.id}" } # FIXME
+ )
+ end
+
+end
diff --git a/server/lib/cimi/models/machine_image_collection.rb b/server/lib/cimi/models/machine_image_collection.rb
new file mode 100644
index 0000000..70bdc7b
--- /dev/null
+++ b/server/lib/cimi/models/machine_image_collection.rb
@@ -0,0 +1,34 @@
+# 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.
+
+class CIMI::Model::MachineImageCollection < CIMI::Model::Base
+
+ act_as_root_entity :machine_image
+
+ array :machine_images do
+ scalar :href
+ end
+
+ def self.default(context)
+ self.new(
+ :id => context.machine_images_url,
+ :name => 'default',
+ :created => Time.now,
+ :description => "#{context.driver.name.capitalize} MachineImageCollection",
+ :machine_images => CIMI::Model::MachineImage.all_uri(context)
+ )
+ end
+
+end
diff --git a/server/lib/cimi/models/machine_template.rb b/server/lib/cimi/models/machine_template.rb
new file mode 100644
index 0000000..8a88052
--- /dev/null
+++ b/server/lib/cimi/models/machine_template.rb
@@ -0,0 +1,41 @@
+# 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.
+
+class CIMI::Model::MachineTemplate < CIMI::Model::Base
+
+ href :machine_config
+ href :machine_image
+ href :machine_admin
+
+ array :volumes do
+ scalar :href
+ scalar :protocol
+ scalar :attachment_point
+ end
+
+ array :volume_templates do
+ scalar :href, :attachment_point, :protocol
+ end
+
+ array :network_interfaces do
+ href :vsp
+ text :hostname, :mac_address, :state, :protocol, :allocation
+ text :address, :default_gateway, :dns, :max_transmission_unit
+ end
+
+ array :operations do
+ scalar :rel, :href
+ end
+end
diff --git a/server/lib/cimi/models/machine_template_collection.rb b/server/lib/cimi/models/machine_template_collection.rb
new file mode 100644
index 0000000..d2a3f23
--- /dev/null
+++ b/server/lib/cimi/models/machine_template_collection.rb
@@ -0,0 +1,34 @@
+# 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.
+
+class CIMI::Model::MachineTemplateCollection < CIMI::Model::Base
+
+ act_as_root_entity :machine_template
+
+ array :machine_templates do
+ scalar :href
+ end
+
+ def self.default(context)
+ self.new(
+ :id => context.machine_template_url,
+ :name => 'default',
+ :created => Time.now,
+ :description => "#{context.driver.name.capitalize} MachineTemplateCollection",
+ :machine_templates => CIMI::Model::MachineTemplate.all_uri(context)
+ )
+ end
+
+end
diff --git a/server/lib/cimi/models/network.rb b/server/lib/cimi/models/network.rb
new file mode 100644
index 0000000..3d9c7ed
--- /dev/null
+++ b/server/lib/cimi/models/network.rb
@@ -0,0 +1,109 @@
+# 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.
+
+class CIMI::Model::Network < CIMI::Model::Base
+
+ text :state
+
+ text :access
+
+ text :bandwidth_limit
+
+ text :traffic_priority
+
+ text :max_traffic_delay
+
+ text :max_traffic_loss
+
+ text :max_traffic_jitter
+
+ href :routing_group
+
+ href :event_log
+
+ array :meters do
+ scalar :href
+ end
+
+ array :operations do
+ scalar :rel, :href
+ end
+
+ def self.find(id, context)
+ networks=[]
+ if id==:all
+ networks = context.driver.networks(context.credentials, {:env=>context})
+ else
+ networks = context.driver.networks(context.credentials, {:id=>id, :env=>context})
+ end
+ networks
+ end
+
+ def self.create(request_body, context, type)
+ input = (type == :xml)? XmlSimple.xml_in(request_body, {"ForceArray"=>false,"NormaliseSpace"=>2}) : JSON.parse(request_body)
+ if input["networkTemplate"]["href"] #template by reference
+ network_config, routing_group = get_by_reference(input, context)
+ else
+ if input["networkTemplate"]["networkConfig"]["href"] # configuration by reference
+ network_config = CIMI::Model::NetworkConfiguration.find(context.href_id(input["networkTemplate"]["networkConfig"]["href"],
+ :network_configurations), context)
+ else #configuration by value
+ network_config = get_by_value(request_body, type)
+ end
+ routing_group = CIMI::Model::RoutingGroup.find(context.href_id(input["networkTemplate"]["routingGroup"]["href"],
+ :routing_groups), context)
+ end
+ params = {:network_config => network_config, :routing_group => routing_group, :name=>input["name"],
+ :description=>input["description"], :env=>context}
+ raise CIMI::Model::BadRequest.new("Bad request - missing required parameters. Client sent: #{request_body} which produced #{params.inspect}") if params.has_value?(nil)
+ context.driver.create_network(context.credentials, params)
+ end
+
+ def self.delete!(id, context)
+ context.driver.delete_network(context.credentials, id)
+ end
+
+ def perform(action, context, &block)
+ begin
+ if context.driver.send(:"#{action.name}_network", context.credentials, self.name)
+ block.callback :success
+ else
+ raise "Operation #{action.name} failed to execute on the Network #{self.name} "
+ end
+ rescue => e
+ block.callback :failure, e.message
+ end
+ end
+
+ private
+
+ def self.get_by_reference(input, context)
+ network_template = CIMI::Model::NetworkTemplate.find(context.href_id(input["networkTemplate"]["href"], :network_templates), context)
+ network_config = CIMI::Model::NetworkConfiguration.find(context.href_id(network_template.network_config.href, :network_configurations), context)
+ routing_group = CIMI::Model::RoutingGroup.find(context.href_id(network_template.routing_group.href, :routing_groups), context)
+ return network_config, routing_group
+ end
+
+ def self.get_by_value(request_body, type)
+ if type == :xml
+ xml_arrays = XmlSimple.xml_in(request_body, {"NormaliseSpace"=>2})
+ network_config = CIMI::Model::NetworkConfiguration.from_xml(XmlSimple.xml_out(xml_arrays["networkTemplate"][0]["networkConfig"][0]))
+ else
+ json = JSON.parse(request_body)
+ network_config = CIMI::Model::NetworkConfiguration.from_json(JSON.generate(json["networkTemplate"]["networkConfig"]))
+ end
+ end
+
+end
diff --git a/server/lib/cimi/models/network_collection.rb b/server/lib/cimi/models/network_collection.rb
new file mode 100644
index 0000000..d76a3dd
--- /dev/null
+++ b/server/lib/cimi/models/network_collection.rb
@@ -0,0 +1,34 @@
+# 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.
+
+class CIMI::Model::NetworkCollection < CIMI::Model::Base
+
+ act_as_root_entity :network
+
+ array :networks do
+ scalar :href
+ end
+
+ def self.default(context)
+ self.new(
+ :id => context.networks_url,
+ :name => 'default',
+ :created => Time.now,
+ :description => "#{context.driver.name.capitalize} NetworkCollection",
+ :networks => CIMI::Model::Network.all(context).map { |c| { :href => c.id } }
+ )
+ end
+
+end
diff --git a/server/lib/cimi/models/network_configuration.rb b/server/lib/cimi/models/network_configuration.rb
new file mode 100644
index 0000000..1b04548
--- /dev/null
+++ b/server/lib/cimi/models/network_configuration.rb
@@ -0,0 +1,49 @@
+# 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.
+
+class CIMI::Model::NetworkConfiguration < CIMI::Model::Base
+
+ text :access
+
+ text :bandwidth_limit
+
+ text :traffic_priority
+
+ text :max_traffic_delay
+
+ text :max_traffic_loss
+
+ text :max_traffic_jitter
+
+ array :operations do
+ scalar :rel, :href
+ end
+
+ def self.find(id, context)
+ network_configs = []
+ if id==:all
+ network_configs = context.driver.network_configurations(context.credentials, {:env=>context})
+ else
+ network_configs = context.driver.network_configurations(context.credentials, {:env=>context, :id=>id})
+ end
+ network_configs
+ end
+
+ def self.create_from_xml(request_body, context)
+ end
+
+ def self.create_from_json(request_body, context)
+ end
+end
diff --git a/server/lib/cimi/models/network_configuration_collection.rb b/server/lib/cimi/models/network_configuration_collection.rb
new file mode 100644
index 0000000..fc14592
--- /dev/null
+++ b/server/lib/cimi/models/network_configuration_collection.rb
@@ -0,0 +1,34 @@
+# 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.
+
+class CIMI::Model::NetworkConfigurationCollection < CIMI::Model::Base
+
+ act_as_root_entity :network_configuration
+
+ array :network_configurations do
+ scalar :href
+ end
+
+ def self.default(context)
+ self.new(
+ :id => context.network_configurations_url,
+ :name => 'default',
+ :created => Time.now,
+ :description => "#{context.driver.name.capitalize} NetworkConfigurationCollection",
+ :network_configurations => CIMI::Model::NetworkConfiguration.all(context).map { |c| { :href => c.id } }
+ )
+ end
+
+end
diff --git a/server/lib/cimi/models/network_template.rb b/server/lib/cimi/models/network_template.rb
new file mode 100644
index 0000000..ce3b990
--- /dev/null
+++ b/server/lib/cimi/models/network_template.rb
@@ -0,0 +1,36 @@
+# 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.
+
+class CIMI::Model::NetworkTemplate < CIMI::Model::Base
+
+ href :network_config
+
+ href :routing_group
+
+ array :operations do
+ scalar :rel, :href
+ end
+
+ def self.find(id, context)
+ network_templates = []
+ if id==:all
+ network_templates = context.driver.network_templates(context.credentials, {:env=>context})
+ else
+ network_templates = context.driver.network_templates(context.credentials, {:env=>context, :id=>id})
+ end
+ network_templates
+ end
+
+end
diff --git a/server/lib/cimi/models/network_template_collection.rb b/server/lib/cimi/models/network_template_collection.rb
new file mode 100644
index 0000000..b2ac537
--- /dev/null
+++ b/server/lib/cimi/models/network_template_collection.rb
@@ -0,0 +1,35 @@
+# 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.
+
+
+class CIMI::Model::NetworkTemplateCollection < CIMI::Model::Base
+
+ act_as_root_entity :network_template
+
+ array :network_templates do
+ scalar :href
+ end
+
+ def self.default(context)
+ self.new(
+ :id => context.network_templates_url,
+ :name => 'default',
+ :created => Time.now,
+ :description => "#{context.driver.name.capitalize} NetworkTemplateCollection",
+ :network_templates => CIMI::Model::NetworkTemplate.all_uri(context)
+ )
+ end
+
+end
diff --git a/server/lib/cimi/models/routing_group.rb b/server/lib/cimi/models/routing_group.rb
new file mode 100644
index 0000000..d26f4d7
--- /dev/null
+++ b/server/lib/cimi/models/routing_group.rb
@@ -0,0 +1,34 @@
+# 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.
+
+class CIMI::Model::RoutingGroup < CIMI::Model::Base
+
+ array :networks do
+ scalar :href
+ end
+
+ array :operations do
+ scalar :rel, :href
+ end
+
+ def self.find(id, context)
+ if id==:all
+ context.driver.routing_groups(context.credentials, {:env=>context})
+ else
+ context.driver.routing_groups(context.credentials, {:env=>context, :id=>id})
+ end
+ end
+
+end
diff --git a/server/lib/cimi/models/routing_group_collection.rb b/server/lib/cimi/models/routing_group_collection.rb
new file mode 100644
index 0000000..285e164
--- /dev/null
+++ b/server/lib/cimi/models/routing_group_collection.rb
@@ -0,0 +1,34 @@
+# 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.
+
+class CIMI::Model::RoutingGroupCollection < CIMI::Model::Base
+
+ act_as_root_entity :routing_group
+
+ array :routing_groups do
+ scalar :href
+ end
+
+ def self.default(context)
+ self.new(
+ :id => context.routing_groups_url,
+ :name => 'default',
+ :created => Time.now,
+ :description => "#{context.driver.name.capitalize} RoutingGroupCollection",
+ :routing_groups => CIMI::Model::RoutingGroup.all_uri(context)
+ )
+ end
+
+end
diff --git a/server/lib/cimi/models/routing_group_template.rb b/server/lib/cimi/models/routing_group_template.rb
new file mode 100644
index 0000000..204a353
--- /dev/null
+++ b/server/lib/cimi/models/routing_group_template.rb
@@ -0,0 +1,34 @@
+# 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.
+
+class CIMI::Model::RoutingGroupTemplate < CIMI::Model::Base
+
+ array :networks do
+ scalar :href
+ end
+
+ array :operations do
+ scalar :rel, :href
+ end
+
+ def self.find(id, context)
+ if id==:all
+ context.driver.routing_group_templates(context.credentials, {:env=>context})
+ else
+ context.driver.routing_group_templates(context.credentials, {:env=>context, :id=>id})
+ end
+ end
+
+end
diff --git a/server/lib/cimi/models/routing_group_template_collection.rb b/server/lib/cimi/models/routing_group_template_collection.rb
new file mode 100644
index 0000000..61a4a68
--- /dev/null
+++ b/server/lib/cimi/models/routing_group_template_collection.rb
@@ -0,0 +1,35 @@
+# 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.
+
+
+class CIMI::Model::RoutingGroupTemplateCollection < CIMI::Model::Base
+
+ act_as_root_entity :routing_group_template
+
+ array :routing_group_templates do
+ scalar :href
+ end
+
+ def self.default(context)
+ self.new(
+ :id => context.routing_group_templates_url,
+ :name => 'default',
+ :created => Time.now,
+ :description => "#{context.driver.name.capitalize} RoutingGroupTemplateCollection",
+ :routing_group_templates => CIMI::Model::RoutingGroupTemplate.all_uri(context)
+ )
+ end
+
+end
diff --git a/server/lib/cimi/models/schema.rb b/server/lib/cimi/models/schema.rb
new file mode 100644
index 0000000..f301b69
--- /dev/null
+++ b/server/lib/cimi/models/schema.rb
@@ -0,0 +1,277 @@
+# 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.
+#
+
+# The smarts of converting from XML and JSON into internal objects
+class CIMI::Model::Schema
+
+ #
+ # Attributes describe how we extract values from XML/JSON
+ #
+ class Attribute
+ attr_reader :name, :xml_name, :json_name
+
+ def initialize(name, opts = {})
+ @name = name
+ @xml_name = (opts[:xml_name] || name).to_s.camelize(true)
+ @json_name = (opts[:json_name] || name).to_s.camelize(true)
+ end
+
+ def from_xml(xml, model)
+ model[@name] = xml[@xml_name].first if xml.has_key?(@xml_name)
+ end
+
+ def from_json(json, model)
+ model[@name] = json[@json_name]
+ end
+
+ def to_xml(model, xml)
+ xml[@xml_name] = [model[@name]] if model[@name]
+ end
+
+ def to_json(model, json)
+ json[@json_name] = model[@name] if model and model[@name]
+ end
+ end
+
+ class Scalar < Attribute
+ def initialize(name, opts)
+ @text = opts[:text]
+ if ! [nil, :nested, :direct].include?(@text)
+ raise "text option for scalar must be :nested or :direct"
+ end
+ super(name, opts)
+ end
+
+ def text?; @text; end
+
+ def nested_text?; @text == :nested; end
+
+ def from_xml(xml, model)
+ case @text
+ when :nested then model[@name] = xml[@xml_name].first["content"] if xml[@xml_name]
+ when :direct then model[@name] = xml["content"]
+ else model[@name] = xml[@xml_name]
+ end
+ end
+
+ def to_xml(model, xml)
+ return unless model
+ return unless model[@name]
+ case @text
+ when :nested then xml[@xml_name] = [{ "content" => model[@name] }]
+ when :direct then xml["content"] = model[@name]
+ else xml[@xml_name] = model[@name]
+ end
+ end
+ end
+
+ class Struct < Attribute
+ def initialize(name, opts, &block)
+ content = opts[:content]
+ super(name, opts)
+ @schema = CIMI::Model::Schema.new
+ @schema.instance_eval(&block) if block_given?
+ @schema.scalar(content, :text => :direct) if content
+ end
+
+ def from_xml(xml, model)
+ xml = xml.has_key?(xml_name) ? xml[xml_name].first : {}
+ model[name] = convert_from_xml(xml)
+ end
+
+ def from_json(json, model)
+ json = json.has_key?(json_name) ? json[json_name] : {}
+ model[name] = convert_from_json(json)
+ end
+
+ def to_xml(model, xml)
+ conv = convert_to_xml(model[name])
+ xml[xml_name] = [conv] unless conv.empty?
+ end
+
+ def to_json(model, json)
+ conv = convert_to_json(model[name])
+ json[json_name] = conv unless conv.empty?
+ end
+
+ def convert_from_xml(xml)
+ sub = struct.new
+ @schema.from_xml(xml, sub)
+ sub
+ end
+
+ def convert_from_json(json)
+ sub = struct.new
+ @schema.from_json(json, sub)
+ sub
+ end
+
+ def convert_to_xml(model)
+ xml = {}
+ @schema.to_xml(model, xml)
+ xml
+ end
+
+ def convert_to_json(model)
+ json = {}
+ @schema.to_json(model, json)
+ json
+ end
+
+ private
+ def struct
+ cname = "CIMI_#{json_name.upcase_first}"
+ if ::Struct.const_defined?(cname)
+ ::Struct.const_get(cname)
+ else
+ ::Struct.new("CIMI_#{json_name.upcase_first}",
+ *@schema.attribute_names)
+ end
+ end
+ end
+
+ class Array < Attribute
+ # For an array :things, we collect all <thing/> elements (XmlSimple
+ # actually does the collecting)
+ def initialize(name, opts = {}, &block)
+ opts[:xml_name] = name.to_s.singularize unless opts[:xml_name]
+ super(name, opts)
+ @struct = Struct.new(name, opts, &block)
+ end
+
+ def from_xml(xml, model)
+ model[name] = (xml[xml_name] || []).map { |elt| @struct.convert_from_xml(elt) }
+ end
+
+ def from_json(json, model)
+ model[name] = (json[json_name] || []).map { |elt| @struct.convert_from_json(elt) }
+ end
+
+ def to_xml(model, xml)
+ ary = (model[name] || []).map { |elt| @struct.convert_to_xml(elt) }
+ xml[xml_name] = ary unless ary.empty?
+ end
+
+ def to_json(model, json)
+ ary = (model[name] || []).map { |elt| @struct.convert_to_json(elt) }
+ json[json_name] = ary unless ary.empty?
+ end
+ end
+
+ class Hash < Attribute
+
+ def initialize(name, opts = {}, &block)
+ opts[:json_name] = name.to_s.pluralize unless opts[:json_name]
+ super(name, opts)
+ @struct = Struct.new(name, opts, &block)
+ end
+
+ def from_xml(xml, model)
+ model[name] = (xml[xml_name] || []).map { |elt| @struct.convert_from_xml(elt) }
+ end
+
+ def from_json(json, model)
+ model[name] = (json[json_name] || {}).inject([]) do |result,item|
+ result << @struct.convert_from_json({ 'name' => item[0], 'value' => item[1] })
+ end
+ end
+
+ def to_xml(model, xml)
+ ary = (model[name] || []).map { |elt| @struct.convert_to_xml(elt) }
+ xml[xml_name] = ary unless ary.empty?
+ end
+
+ def to_json(model, json)
+ ary = (model[name] || []).map { |elt| @struct.convert_to_json(elt) }
+ json[json_name] = ary.inject({}) { |result, item| result[item['name']] = item['value']; result } unless ary.empty?
+ end
+ end
+
+ #
+ # The actual Schema class
+ #
+ def initialize
+ @attributes = []
+ end
+
+ def from_xml(xml, model = {})
+ @attributes.freeze
+ @attributes.each { |attr| attr.from_xml(xml, model) }
+ model
+ end
+
+ def from_json(json, model = {})
+ @attributes.freeze
+ @attributes.each { |attr| attr.from_json(json, model) }
+ model
+ end
+
+ def to_xml(model, xml = {})
+ @attributes.freeze
+ @attributes.each { |attr| attr.to_xml(model, xml) }
+ xml
+ end
+
+ def to_json(model, json = {})
+ @attributes.freeze
+ @attributes.each { |attr| attr.to_json(model, json) }
+ json
+ end
+
+ def attribute_names
+ @attributes.map { |a| a.name }
+ end
+
+ #
+ # The DSL
+ #
+ # Requires that the class into which this is included has a
+ # +add_attributes!+ method
+ module DSL
+ def href(*args)
+ args.each { |arg| struct(arg) { scalar :href } }
+ end
+
+ def text(*args)
+ args.expand_opts!(:text => :nested)
+ scalar(*args)
+ end
+
+ def scalar(*args)
+ add_attributes!(args, Scalar)
+ end
+
+ def array(name, opts={}, &block)
+ add_attributes!([name, opts], Array, &block)
+ end
+
+ def struct(name, opts={}, &block)
+ add_attributes!([name, opts], Struct, &block)
+ end
+
+ def hash(name, opts={}, &block)
+ add_attributes!([name, opts], Hash, &block)
+ end
+ end
+
+ include DSL
+
+ def add_attributes!(args, attr_klass, &block)
+ raise "The schema has already been used to convert objects" if @attributes.frozen?
+ opts = args.extract_opts!
+ args.each { |arg| @attributes << attr_klass.new(arg, opts, &block) }
+ end
+end
diff --git a/server/lib/cimi/models/volume.rb b/server/lib/cimi/models/volume.rb
new file mode 100644
index 0000000..9c106e2
--- /dev/null
+++ b/server/lib/cimi/models/volume.rb
@@ -0,0 +1,103 @@
+# 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.
+
+class CIMI::Model::Volume < CIMI::Model::Base
+
+ struct :capacity do
+ scalar :quantity
+ scalar :units
+ end
+ text :bootable
+ text :supports_snapshots
+ array :snapshots do
+ scalar :ref
+ end
+ text :guest_interface
+ array :meters do
+ scalar :ref
+ end
+ href :eventlog
+ array :operations do
+ scalar :rel, :href
+ end
+
+ def self.find(id, context)
+ volumes = []
+ opts = ( id == :all ) ? {} : { :id => id }
+ volumes = context.driver.storage_volumes(context.credentials, opts)
+ volumes.collect!{ |volume| from_storage_volume(volume, context) }
+ return volumes.first unless volumes.length > 1
+ return volumes
+ end
+
+ def self.all(context); find(:all, context); end
+
+ def self.create_from_json(json_in, context)
+ json = JSON.parse(json_in)
+ volume_config_id = json["volumeTemplate"]["volumeConfig"]["href"].split("/").last
+ volume_image_id = (json["volumeTemplate"].has_key?("volumeImage") ?
+ json["volumeTemplate"]["volumeImage"]["href"].split("/").last : nil)
+ create_volume({:volume_config_id=>volume_config_id, :volume_image_id=>volume_image_id}, context)
+ end
+
+ def self.create_from_xml(xml_in, context)
+ xml = XmlSimple.xml_in(xml_in)
+ volume_config_id = xml["volumeTemplate"][0]["volumeConfig"][0]["href"].split("/").last
+ volume_image_id = (xml["volumeTemplate"][0].has_key?("volumeImage") ?
+ xml["volumeTemplate"][0]["volumeImage"][0]["href"].split("/").last : nil)
+ create_volume({:volume_config_id=>volume_config_id, :volume_image_id=>volume_image_id}, context)
+ end
+
+ def self.delete!(id, context)
+ context.driver.destroy_storage_volume(context.credentials, {:id=>id} )
+ end
+
+ def self.find_to_attach_from_json(json_in, context)
+ json = JSON.parse(json_in)
+ volumes = json["volumes"].map{|v| {:volume=>self.find(v["volume"]["href"].split("/volumes/").last, context),
+ :attachment_point=>v["attachmentPoint"] }}
+ end
+
+ def self.find_to_attach_from_xml(xml_in, context)
+ xml = XmlSimple.xml_in(xml_in)
+ volumes = xml["volume"].map{|v| {:volume => self.find(v["href"].split("/volumes/").last, context),
+ :attachment_point=>v["attachmentPoint"] }}
+ end
+
+ private
+
+ def self.create_volume(params, context)
+ volume_config = CIMI::Model::VolumeConfiguration.find(params[:volume_config_id], context)
+ opts = {:capacity=>volume_config.capacity[:quantity], :snapshot_id=>params[:volume_image_id] }
+ storage_volume = self.driver.create_storage_volume(context.credentials, opts)
+ from_storage_volume(storage_volume, context)
+ end
+
+ def self.from_storage_volume(volume, context)
+ self.new( { :name => volume.id,
+ :description => volume.id,
+ :created => volume.created,
+ :id => context.volume_url(volume.id),
+ :capacity => { :quantity=>volume.capacity, :units=>"gibibyte" }, #FIXME... units will vary
+ :bootable => "false", #fixme ... will vary... ec2 doesn't expose this
+ :supports_snapshots => "true", #fixme, will vary (true for ec2)
+ :snapshots => [], #fixme...
+ :guest_interface => "",
+ :eventlog => {:href=> "http://eventlogs"},#FIXME
+ :meters => []
+ } )
+ end
+
+end
diff --git a/server/lib/cimi/models/volume_collection.rb b/server/lib/cimi/models/volume_collection.rb
new file mode 100644
index 0000000..1f4152e
--- /dev/null
+++ b/server/lib/cimi/models/volume_collection.rb
@@ -0,0 +1,34 @@
+# 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.
+
+class CIMI::Model::VolumeCollection < CIMI::Model::Base
+
+ act_as_root_entity :volume
+
+ array :volumes do
+ scalar :href
+ end
+
+ def self.default(context)
+ self.new(
+ :id => context.volumes_url,
+ :name => 'default',
+ :created => Time.now,
+ :description => "#{context.driver.name.capitalize} VolumeCollection",
+ :volumes => CIMI::Model::Volume.all_uri(context)
+ )
+ end
+
+end
diff --git a/server/lib/cimi/models/volume_configuration.rb b/server/lib/cimi/models/volume_configuration.rb
new file mode 100644
index 0000000..75b37ea
--- /dev/null
+++ b/server/lib/cimi/models/volume_configuration.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.
+
+class CIMI::Model::VolumeConfiguration < CIMI::Model::Base
+
+ text :format
+ struct :capacity do
+ scalar :quantity
+ scalar :units
+ end
+ text :supports_snapshots
+ text :guest_interface
+ array :operations do
+ scalar :rel, :href
+ end
+
+ def self.find(id, context)
+ volume_configs = []
+ if id == :all
+ #ec2 ebs volumes can 1gb..1tb
+ (1..1000).each do |size|
+ volume_configs << create(size, context)
+ end
+ else
+ volume_configs << create(id, context)
+ return volume_configs.first
+ end
+ return volume_configs
+ end
+
+
+ def self.all(context); find(:all, context); end
+
+ private
+
+ def self.create(size, context)
+ self.new( {
+ :id => context.volume_configuration_url(size),
+ :name => size,
+ :description => "volume configuration with #{size} GiB",
+ :created => Time.now.to_s,
+ :capacity => {:quantity=>size, :units=>"gibibytes"},
+ :supports_snapshots => "true"
+ # FIXME :guest_interface => "NFS"
+ } )
+ end
+
+end
diff --git a/server/lib/cimi/models/volume_configuration_collection.rb b/server/lib/cimi/models/volume_configuration_collection.rb
new file mode 100644
index 0000000..2120ae6
--- /dev/null
+++ b/server/lib/cimi/models/volume_configuration_collection.rb
@@ -0,0 +1,34 @@
+# 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.
+
+class CIMI::Model::VolumeConfigurationCollection < CIMI::Model::Base
+
+ act_as_root_entity :volume_configuration
+
+ array :volume_configurations do
+ scalar :href
+ end
+
+ def self.default(context)
+ self.new(
+ :id => context.volume_configurations_url,
+ :name => 'default',
+ :created => Time.now,
+ :description => "#{context.driver.name.capitalize} VolumeConfigurationCollection",
+ :volume_configurations => CIMI::Model::VolumeConfiguration.all_uri(context)
+ )
+ end
+
+end
diff --git a/server/lib/cimi/models/volume_image.rb b/server/lib/cimi/models/volume_image.rb
new file mode 100644
index 0000000..03cc7fd
--- /dev/null
+++ b/server/lib/cimi/models/volume_image.rb
@@ -0,0 +1,49 @@
+# 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.
+
+class CIMI::Model::VolumeImage < CIMI::Model::Base
+
+ href :image_location
+ text :image_data
+ text :bootable
+ array :operations do
+ scalar :rel, :href
+ end
+
+ def self.find(id, context)
+ storage_snapshots = []
+ opts = ( id==:all ) ? {} : { :id=>id }
+ storage_snapshots = self.driver.storage_snapshots(context.credentials, opts)
+ storage_snapshots.collect!{ |snapshot| from_storage_snapshot(snapshot, context) }
+ return storage_snapshots.first unless storage_snapshots.length > 1
+ return storage_snapshots
+ end
+
+ def self.all(context); find(:all, context); end
+
+ private
+
+ def self.from_storage_snapshot(snapshot, context)
+ self.new( {
+ :name => snapshot.id,
+ :description => snapshot.id,
+ :created => snapshot.created,
+ :id => context.volume_image_url(snapshot.id),
+ :image_location => {:href=>context.volume_url(snapshot.storage_volume_id)},
+ :bootable => "false" #FIXME
+ } )
+ end
+
+end
diff --git a/server/lib/cimi/models/volume_image_collection.rb b/server/lib/cimi/models/volume_image_collection.rb
new file mode 100644
index 0000000..f3da877
--- /dev/null
+++ b/server/lib/cimi/models/volume_image_collection.rb
@@ -0,0 +1,34 @@
+# 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.
+
+class CIMI::Model::VolumeImageCollection < CIMI::Model::Base
+
+ act_as_root_entity :volume_image
+
+ array :volume_images do
+ scalar :href
+ end
+
+ def self.default(context)
+ self.new(
+ :id => context.volume_images_url,
+ :name => 'default',
+ :created => Time.now,
+ :description => "#{context.driver.name.capitalize} VolumeImageCollection",
+ :volume_images => CIMI::Model::VolumeImage.all_uri(context)
+ )
+ end
+
+end
diff --git a/server/lib/cimi/models/volume_template.rb b/server/lib/cimi/models/volume_template.rb
new file mode 100644
index 0000000..b9c82db
--- /dev/null
+++ b/server/lib/cimi/models/volume_template.rb
@@ -0,0 +1,23 @@
+# 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.
+
+class CIMI::Model::VolumeTemplate < CIMI::Model::Base
+
+ href :volume_config
+ href :volume_image
+ array :operations do
+ scalar :rel, :href
+ end
+end
diff --git a/server/lib/cimi/models/volume_template_collection.rb b/server/lib/cimi/models/volume_template_collection.rb
new file mode 100644
index 0000000..f53547c
--- /dev/null
+++ b/server/lib/cimi/models/volume_template_collection.rb
@@ -0,0 +1,34 @@
+# 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.
+
+class CIMI::Model::VolumeTemplateCollection < CIMI::Model::Base
+
+ act_as_root_entity :volume_template
+
+ array :volume_templates do
+ scalar :href
+ end
+
+ def self.default(context)
+ self.new(
+ :id => context.volume_template_url,
+ :name => 'default',
+ :created => Time.now,
+ :description => "#{context.driver.name.capitalize} VolumeTemplateCollection",
+ :volume_templates => CIMI::Model::VolumeTemplate.all_uri(context)
+ )
+ end
+
+end
diff --git a/server/lib/cimi/models/vsp.rb b/server/lib/cimi/models/vsp.rb
new file mode 100644
index 0000000..b8b27f5
--- /dev/null
+++ b/server/lib/cimi/models/vsp.rb
@@ -0,0 +1,102 @@
+# 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.
+
+class CIMI::Model::VSP < CIMI::Model::Base
+
+ text :state
+
+ href :network
+
+ text :bandwidth_reservation
+
+ text :traffic_priority
+
+ text :max_traffic_delay
+
+ text :max_traffic_loss
+
+ text :max_traffic_jitter
+
+ href :event_log
+
+ array :meters do
+ scalar :href
+ end
+
+ array :operations do
+ scalar :rel, :href
+ end
+
+ def self.find(id, context)
+ if id==:all
+ context.driver.vsps(context.credentials, {:env=>context})
+ else
+ context.driver.vsps(context.credentials, {:id=>id, :env=>context})
+ end
+ end
+
+ def self.create(request_body, context, type)
+ input = (type == :xml)? XmlSimple.xml_in(request_body, {"ForceArray"=>false, "NormaliseSpace"=>2}) : JSON.parse(request_body)
+ if input["vspTemplate"]["href"] #template by reference
+ vsp_config, network = get_by_reference(input, context)
+ else
+ if input["vspTemplate"]["vspConfig"]["href"] # configuration by reference
+ vsp_config = CIMI::Model::VSPConfiguration.find(context.href_id(input["vspTemplate"]["vspConfig"]["href"],:vsp_configurations), context)
+ else #configuration by value
+ vsp_config = get_by_value(request_body, type)
+ end
+ network = CIMI::Model::Network.find(context.href_id(input["vspTemplate"]["network"]["href"], :networks), context)
+ end
+ params = {:vsp_config => vsp_config, :network => network, :name=>input["name"], :description=>input["description"], :env=>context}
+ raise CIMI::Model::BadRequest.new("Bad request - missing required parameters. Client sent: #{request_body} which produced #{params.inspect}") if params.has_value?(nil)
+ context.driver.create_vsp(context.credentials, params)
+ end
+
+ def self.delete!(id, context)
+ context.driver.delete_vsp(context.credentials, id)
+ end
+
+ def perform(action, context, &block)
+ begin
+ if context.driver.send(:"#{action.name}_vsp", context.credentials, self.name)
+ block.callback :success
+ else
+ raise "Operation #{action.name} failed to execute on the VSP #{self.name} "
+ end
+ rescue => e
+ block.callback :failure, e.message
+ end
+ end
+
+
+ private
+
+ def self.get_by_reference(input, context)
+ vsp_template = CIMI::Model::VSPTemplate.find(context.href_id(input["vspTemplate"]["href"], :vsp_templates), context)
+ vsp_config = CIMI::Model::VSPConfiguration.find(context.href_id(vsp_template.vsp_config.href, :vsp_configurations), context)
+ network = CIMI::Model::Network.find(context.href_id(vsp_template.network.href, :networks), context)
+ return vsp_config, network
+ end
+
+ def self.get_by_value(request_body, type)
+ if type == :xml
+ xml_arrays = XmlSimple.xml_in(request_body, {"NormaliseSpace"=>2})
+ vsp_config = CIMI::Model::VSPConfiguration.from_xml(XmlSimple.xml_out(xml_arrays["vspTemplate"][0]["vspConfig"][0]))
+ else
+ json = JSON.parse(request_body)
+ vsp_config = CIMI::Model::VSPConfiguration.from_json(JSON.generate(json["vspTemplate"]["vspConfig"]))
+ end
+ end
+end
diff --git a/server/lib/cimi/models/vsp_collection.rb b/server/lib/cimi/models/vsp_collection.rb
new file mode 100644
index 0000000..fc72024
--- /dev/null
+++ b/server/lib/cimi/models/vsp_collection.rb
@@ -0,0 +1,34 @@
+# 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.
+
+class CIMI::Model::VSPCollection < CIMI::Model::Base
+
+ CIMI::Model.register_as_root_entity! "VSPs"
+
+ array :vsps do
+ scalar :href
+ end
+
+ def self.default(context)
+ self.new(
+ :id => context.vsps_url,
+ :name => 'default',
+ :created => Time.now,
+ :description => "#{context.driver.name.capitalize} VSPCollection",
+ :vsps => CIMI::Model::VSP.all_uri(context)
+ )
+ end
+
+end
diff --git a/server/lib/cimi/models/vsp_configuration.rb b/server/lib/cimi/models/vsp_configuration.rb
new file mode 100644
index 0000000..c9a9bf3
--- /dev/null
+++ b/server/lib/cimi/models/vsp_configuration.rb
@@ -0,0 +1,40 @@
+# 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.
+
+class CIMI::Model::VSPConfiguration < CIMI::Model::Base
+
+ text :bandwidth_reservation
+
+ text :traffic_priority
+
+ text :max_traffic_delay
+
+ text :max_traffic_loss
+
+ text :max_traffic_jitter
+
+ array :operations do
+ scalar :rel, :href
+ end
+
+ def self.find(id, context)
+ if id==:all
+ context.driver.vsp_configurations(context.credentials, {:env=>context})
+ else
+ context.driver.vsp_configurations(context.credentials, {:env=>context, :id=>id})
+ end
+ end
+
+end
diff --git a/server/lib/cimi/models/vsp_configuration_collection.rb b/server/lib/cimi/models/vsp_configuration_collection.rb
new file mode 100644
index 0000000..d4927e7
--- /dev/null
+++ b/server/lib/cimi/models/vsp_configuration_collection.rb
@@ -0,0 +1,34 @@
+# 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.
+
+class CIMI::Model::VSPConfigurationCollection < CIMI::Model::Base
+
+ CIMI::Model.register_as_root_entity! "VSPConfigurations"
+
+ array :vsp_configurations do
+ scalar :href
+ end
+
+ def self.default(context)
+ self.new(
+ :id => context.vsp_configurations_url,
+ :name => 'default',
+ :created => Time.now,
+ :description => "#{context.driver.name.capitalize} VSPConfigurationCollection",
+ :vsp_configurations => CIMI::Model::VSPConfiguration.all_uri(context)
+ )
+ end
+
+end
diff --git a/server/lib/cimi/models/vsp_template.rb b/server/lib/cimi/models/vsp_template.rb
new file mode 100644
index 0000000..f1b8078
--- /dev/null
+++ b/server/lib/cimi/models/vsp_template.rb
@@ -0,0 +1,34 @@
+# 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.
+
+class CIMI::Model::VSPTemplate < CIMI::Model::Base
+
+ href :network
+
+ href :vsp_config
+
+ array :operations do
+ scalar :rel, :href
+ end
+
+ def self.find(id, context)
+ if id==:all
+ context.driver.vsp_templates(context.credentials, {:env=>context})
+ else
+ context.driver.vsp_templates(context.credentials, {:env=>context, :id=>id})
+ end
+ end
+
+end
diff --git a/server/lib/cimi/models/vsp_template_collection.rb b/server/lib/cimi/models/vsp_template_collection.rb
new file mode 100644
index 0000000..61d5311
--- /dev/null
+++ b/server/lib/cimi/models/vsp_template_collection.rb
@@ -0,0 +1,34 @@
+# 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.
+
+class CIMI::Model::VSPTemplateCollection < CIMI::Model::Base
+
+ CIMI::Model.register_as_root_entity! "VSPTemplates"
+
+ array :vsp_templates do
+ scalar :href
+ end
+
+ def self.default(context)
+ self.new(
+ :id => context.vsp_templates_url,
+ :name => 'default',
+ :created => Time.now,
+ :description => "#{context.driver.name.capitalize} VSPTemplateCollection",
+ :vsp_templates => CIMI::Model::VSPTemplate.all_uri(context)
+ )
+ end
+
+end
diff --git a/server/lib/cimi/server.rb b/server/lib/cimi/server.rb
index 60f9a3b..a1c7ef9 100644
--- a/server/lib/cimi/server.rb
+++ b/server/lib/cimi/server.rb
@@ -13,947 +13,48 @@
# License for the specific language governing permissions and limitations
# under the License.
-require 'cimi/dependencies'
-require 'cimi/helpers/cimi_helper'
-require 'cimi/model'
+require 'rubygems'
+require 'crack'
+require 'json'
+require 'yaml'
+require 'haml'
+require 'sinatra/base'
+require 'sinatra/rabbit'
+require_relative '../sinatra'
-set :version, '0.1.0'
+require_relative './helpers'
+require_relative './collections'
-include Deltacloud::Drivers
-include CIMI::Model
+CMWG_NAMESPACE = "http://www.dmtf.org/cimi"
-set :drivers, Proc.new { driver_config }
+module CIMI
+ class API < Collections::Base
-Sinatra::Application.register Rack::RespondTo
+ # Enable logging
+ use Rack::CommonLogger
+ use Rack::Date
+ use Rack::ETag
+ use Rack::MatrixParams
+ use Rack::DriverSelect
+ use Rack::Accept
+ use Rack::MediaType
-use Rack::ETag
-use Rack::Runtime
-use Rack::MatrixParams
-use Rack::DriverSelect
-use Rack::MediaType
-use Rack::Date
-use Rack::CIMI
+ helpers CIMIHelper
-configure do
- set :root_url, "/cimi"
- set :views, File::join($top_srcdir, 'views', 'cimi')
- set :public_folder, File::join($top_srcdir, 'public')
- driver
-end
-
-configure :production do
- use Rack::SyslogLogger
- disable :logging
- enable :show_errors
- set :dump_errors, false
- $stdout = SyslogFile.new
- $stderr = $stdout
-end
-
-configure :development do
- set :raise_errors => false
- set :show_exceptions, false
- $stdout.sync = true
- $stderr.sync = true
-end
-
-# You could use $API_HOST environment variable to change your hostname to
-# whatever you want (eg. if you running API behind NAT)
-HOSTNAME=ENV['API_HOST'] ? ENV['API_HOST'] : nil
-
-error do
- report_error
-end
-
-get "/" do
- redirect settings.root_url
-end
-
-get "#{settings.root_url}\/?" do
- halt 401 if params[:force_auth] and not driver.valid_credentials?(credentials)
- redirect cloudEntryPoint_url, 301
-end
+ include Deltacloud::Helpers
+ include CIMI::Collections
+ include CIMI::Model
-global_collection :cloudEntryPoint do
- description 'Cloud entry point'
- operation :index do
- description "list all resources of the cloud"
- control do
- entry_point = CloudEntryPoint.create(self)
+ get API_ROOT_URL do
+ if params[:force_auth]
+ return [401, 'Authentication failed'] unless driver.valid_credentials?(credentials)
+ end
+ entry_point = CIMI::Model::CloudEntryPoint.create(self)
respond_to do |format|
format.xml { entry_point.to_xml }
format.json { entry_point.to_json }
end
end
- end
-end
-
-global_collection :machine_configurations do
- description 'List all machine configurations'
-
- operation :index do
- param :CIMISelect, :string, :optional
- description "List all machine configurations"
- control do
- machine_configs = MachineConfigurationCollection.default(self).filter_by(params[:CIMISelect])
- respond_to do |format|
- format.xml { machine_configs.to_xml }
- format.json { machine_configs.to_json }
- end
- end
- end
-
- operation :show do
-
- description "The Machine Configuration entity represents the set of configuration values "+
- "that define the (virtual) hardware resources of a to-be-realized Machine Instance.."
-
- param :id, :string, :required
-
- control do
- machine_conf = MachineConfiguration.find(params[:id], self)
- respond_to do |format|
- format.xml { machine_conf.to_xml }
- format.json { machine_conf.to_json }
- end
- end
-
- end
-end
-
-global_collection :machine_images do
- description 'List all machine images'
-
- operation :index do
- description "List all machine configurations"
- param :CIMISelect, :string, :optional
- control do
- machine_images = MachineImageCollection.default(self).filter_by(params[:CIMISelect])
- respond_to do |format|
- format.xml { machine_images.to_xml }
- format.json { machine_images.to_json }
- end
- end
- end
-
- operation :show do
- description "Show specific machine image."
- param :id, :string, :required
- control do
- machine_image = MachineImage.find(params[:id], self)
- respond_to do |format|
- format.xml { machine_image.to_xml }
- format.json { machine_image.to_json }
- end
- end
- end
-
-end
-
-global_collection :machine_admins do
- description 'Machine Admin entity'
-
- operation :index do
- description "List all machine admins"
- param :CIMISelect, :string, :optional
- with_capability :keys
- control do
- machine_admins = MachineAdminCollection.default(self).filter_by(params[:CIMISelect])
- respond_to do |format|
- format.xml { machine_admins.to_xml }
- format.json { machine_admins.to_json }
- end
- end
- end
-
- operation :show do
- description "Show specific machine admin"
- param :id, :string, :required
- with_capability :key
- control do
- machine_admin = MachineAdmin.find(params[:id], self)
- respond_to do |format|
- format.xml { machine_admin.to_xml }
- format.json { machine_admin.to_json }
- end
- end
- end
-
- operation :create do
- description "Show specific machine admin"
- with_capability :create_key
- control do
- if request.content_type.end_with?("+json")
- new_admin = MachineAdmin.create_from_json(request.body.read, self)
- else
- new_admin = MachineAdmin.create_from_xml(request.body.read, self)
- end
- status 201 # Created
- respond_to do |format|
- format.json { new_admin.to_json }
- format.xml { new_admin.to_xml }
- end
- end
- end
-
- operation :delete, :method => :delete, :member => true do
- description "Delete specified MachineAdmin entity"
- param :id, :string, :required
- control do
- MachineAdmin.delete!(params[:id], self)
- no_content_with_status(200)
- end
- end
-
-end
-
-global_collection :machines do
- description 'List all machine'
-
- operation :index do
- param :CIMISelect, :string, :optional
- description "List all machines"
- control do
- machines = MachineCollection.default(self).filter_by(params[:CIMISelect])
- respond_to do |format|
- format.xml { machines.to_xml }
- format.json { machines.to_json }
- end
- end
- end
-
- operation :show do
- description "Show specific machine."
- param :id, :string, :required
- control do
- machine = Machine.find(params[:id], self)
- respond_to do |format|
- format.xml { machine.to_xml }
- format.json { machine.to_json }
- end
- end
- end
- operation :create do
- description "Create a new Machine entity."
- control do
- if request.content_type.end_with?("+json")
- new_machine = Machine.create_from_json(request.body.read, self)
- else
- new_machine = Machine.create_from_xml(request.body.read, self)
- end
- status 201 # Created
- respond_to do |format|
- format.json { new_machine.to_json }
- format.xml { new_machine.to_xml }
- end
- end
end
-
- operation :destroy do
- description "Delete a specified machine."
- param :id, :string, :required
- control do
- Machine.delete!(params[:id], self)
- no_content_with_status(200)
- end
- end
-
- operation :stop, :method => :post, :member => true do
- description "Stop specific machine."
- param :id, :string, :required
- control do
- machine = Machine.find(params[:id], self)
- if request.content_type.end_with?("+json")
- action = Action.from_json(request.body.read)
- else
- action = Action.from_xml(request.body.read)
- end
- machine.perform(action, self) do |operation|
- no_content_with_status(202) if operation.success?
- # Handle errors using operation.failure?
- end
- end
- end
-
- operation :restart, :method => :post, :member => true do
- description "Start specific machine."
- param :id, :string, :required
- control do
- machine = Machine.find(params[:id], self)
- if request.content_type.end_with?("+json")
- action = Action.from_json(request.body.read)
- else
- action = Action.from_xml(request.body.read)
- end
- machine.perform(action, self) do |operation|
- no_content_with_status(202) if operation.success?
- # Handle errors using operation.failure?
- end
- end
- end
-
- operation :start, :method => :post, :member => true do
- description "Start specific machine."
- param :id, :string, :required
- control do
- machine = Machine.find(params[:id], self)
- if request.content_type.end_with?("+json")
- action = Action.from_json(request.body.read)
- else
- action = Action.from_xml(request.body.read)
- end
- machine.perform(action, self) do |operation|
- no_content_with_status(202) if operation.success?
- # Handle errors using operation.failure?
- end
- end
- end
-
-#NOTE: The routes for attach/detach used here are NOT as specified by CIMI
-#will likely move later. CIMI specifies PUT of the whole Machine description
-#with inclusion/ommission of the volumes you want [att|det]ached
- operation :attach_volume, :method => :put, :member => true do
- description "Attach CIMI Volume(s) to a machine."
- param :id, :string, :required
- control do
- if request.content_type.end_with?("+json")
- volumes_to_attach = Volume.find_to_attach_from_json(request.body.read, self)
- else
- volumes_to_attach = Volume.find_to_attach_from_xml(request.body.read, self)
- end
- machine = Machine.attach_volumes(volumes_to_attach, self)
- respond_to do |format|
- format.json{ machine.to_json}
- format.xml{machine.to_xml}
- end
- end
- end
-
- operation :detach_volume, :method => :put, :member => true do
- description "Detach CIMI Volume(s) from a machine."
- param :id, :string, :required
- control do
- if request.content_type.end_with?("+json")
- volumes_to_detach = Volume.find_to_attach_from_json(request.body.read, self)
- else
- volumes_to_detach = Volume.find_to_attach_from_xml(request.body.read, self)
- end
- machine = Machine.detach_volumes(volumes_to_detach, self)
- respond_to do |format|
- format.json{ machine.to_json}
- format.xml{machine.to_xml}
- end
- end
- end
-end
-
-global_collection :volumes do
- description "Volume represents storage at either the block or file-system level. Volumes can be attached to Machines. Once attached, Volumes can be accessed by processes on that Machine"
-
- operation :index do
- description "List all volumes"
- param :CIMISelect, :string, :optional
- control do
- volumes = VolumeCollection.default(self).filter_by(params[:CIMISelect])
- respond_to do |format|
- format.xml { volumes.to_xml }
- format.json { volumes.to_json }
- end
- end
- end
-
- operation :show do
- description "Show specific Volume."
- param :id, :string, :required
- control do
- volume = Volume.find(params[:id], self)
- if volume
- respond_to do |format|
- format.xml { volume.to_xml }
- format.json { volume.to_json }
- end
- else
- report_error(404)
- end
- end
- end
-
- operation :create do
- description "Create a new Volume."
- control do
- content_type = (request.content_type.end_with?("+json") ? :json : :xml)
- #((request.content_type.end_with?("+xml")) ? :xml : report_error(415) ) FIXME
- case content_type
- when :json
- new_volume = Volume.create_from_json(request.body.read, self)
- when :xml
- new_volume = Volume.create_from_xml(request.body.read, self)
- end
- respond_to do |format|
- format.json { new_volume.to_json }
- format.xml { new_volume.to_xml }
- end
- end
- end
-
- operation :destroy do
- description "Delete a specified Volume"
- param :id, :string, :required
- control do
- Volume.delete!(params[:id], self)
- no_content_with_status(200)
- end
- end
-
-end
-
-global_collection :volume_configurations do
- description "The Volume Configuration entity represents the set of configuration values needed to create a Volume with certain characteristics. Volume Configurations are created by Providers and MAY, at the Providers discretion, be created by Consumers"
-
- operation :index do
- description "Get list all VolumeConfigurations"
- param :CIMISelect, :string, :optional
- control do
- volume_configuration = VolumeConfigurationCollection.default(self).filter_by(params[:CIMISelect])
- respond_to do |format|
- format.xml { volume_configuration.to_xml }
- format.json { volume_configuration.to_json }
- end
- end
- end
-
- operation :show do
- description "Get a specific VolumeConfiguration"
- param :id, :required, :string
- control do
- volume_config = VolumeConfiguration.find(params[:id], self)
- respond_to do |format|
- format.xml { volume_config.to_xml }
- format.json { volume_config.json }
- end
- end
- end
-
-global_collection :volume_images do
- description 'This entity represents an image that could be place on a pre-loaded volume.'
-
- operation :index do
- description "List all volumes images"
- param :CIMISelect, :string, :optional
- control do
- volume_images = VolumeImageCollection.default(self).filter_by(params[:CIMISelect])
- respond_to do |format|
- format.xml { volume_images.to_xml }
- format.json { volume_images.to_json }
- end
- end
- end
-
- operation :show do
- description "Show a specific volume image"
- param :id, :string, :required
- control do
- volume_image = VolumeImage.find(params[:id], self)
- respond_to do |format|
- format.xml { volume_image.to_xml }
- format.json { volume_image.to_json }
- end
- end
- end
-
-end
-
-
-global_collection :entity_metadata do
- description 'This allows for the discovery of Provider defined constraints on the CIMI defined attributes as well as discovery of any new extension attributes that the Provider may have defined.'
-
- operation :index do
- description "List all entity metadata defined for this provider"
- control do
- entity_metadata = EntityMetadataCollection.default(self)
- respond_to do |format|
- format.xml{entity_metadata.to_xml}
- format.json{entity_metadata.to_json}
- end
- end
- end
-
- operation :show do
- description "Get the entity metadata for a specific collection"
- param :id, :required, :string
- control do
- entity_metadata = EntityMetadata.find(params[:id], self)
- respond_to do |format|
- format.xml{entity_metadata.to_xml}
- format.json{entity_metadata.to_json}
- end
- end
- end
-
-end
-
-global_collection :networks do
- description 'A Network represents an abstraction of a layer 2 broadcast domain'
-
- operation :index do
- description "List all Networks"
- param :CIMISelect, :string, :optional
- control do
- networks = NetworkCollection.default(self).filter_by(params[:CIMISelect])
- respond_to do |format|
- format.xml { networks.to_xml }
- format.json { networks.to_json }
- end
- end
- end
-
- operation :show do
- description "Show a specific Network"
- param :id, :string, :required
- control do
- network = Network.find(params[:id], self)
- respond_to do |format|
- format.xml { network.to_xml }
- format.json { network.to_json }
- end
- end
- end
-
- operation :create do
- description "Create a new Network"
- control do
- if request.content_type.end_with?("json")
- network = Network.create(request.body.read, self, :json)
- else
- network = Network.create(request.body.read, self, :xml)
- end
- respond_to do |format|
- format.xml { network.to_xml}
- format.json { network.to_json }
- end
- end
- end
-
- operation :destroy do
- description "Delete a specified Network"
- param :id, :string, :required
- control do
- Network.delete!(params[:id], self)
- no_content_with_status(200)
- end
- end
-
- operation :start, :method => :post, :member => true do
- description "Start specific network."
- param :id, :string, :required
- control do
- network = Network.find(params[:id], self)
- report_error(404) unless network
- if request.content_type.end_with?("json")
- action = Action.from_json(request.body.read)
- else
- action = Action.from_xml(request.body.read)
- end
- network.perform(action, self) do |operation|
- no_content_with_status(202) if operation.success?
- # Handle errors using operation.failure?
- end
- end
- end
-
- operation :stop, :method => :post, :member => true do
- description "Stop specific network."
- param :id, :string, :required
- control do
- network = Network.find(params[:id], self)
- report_error(404) unless network
- if request.content_type.end_with?("json")
- action = Action.from_json(request.body.read)
- else
- action = Action.from_xml(request.body.read)
- end
- network.perform(action, self) do |operation|
- no_content_with_status(202) if operation.success?
- # Handle errors using operation.failure?
- end
- end
- end
-
- operation :suspend, :method => :post, :member => true do
- description "Suspend specific network."
- param :id, :string, :required
- control do
- network = Network.find(params[:id], self)
- report_error(404) unless network
- if request.content_type.end_with?("json")
- action = Action.from_json(request.body.read)
- else
- action = Action.from_xml(request.body.read)
- end
- network.perform(action, self) do |operation|
- no_content_with_status(202) if operation.success?
- # Handle errors using operation.failure?
- end
- end
- end
-
-end
-
-global_collection :network_configurations do
- description 'Network Configurations contain the set of configuration values representing the information needed to create a Network with certain characteristics'
-
- operation :index do
- description 'List all NetworkConfigurations'
- param :CIMISelect, :string, :optional
- control do
- network_configurations = NetworkConfigurationCollection.default(self).filter_by(params[:CIMISelect])
- respond_to do |format|
- format.xml { network_configurations.to_xml }
- format.json { network_configurations.to_json }
- end
- end
- end
-
- operation :show do
- description 'Show a specific NetworkConfiguration'
- param :id, :string, :required
- control do
- network_config = NetworkConfiguration.find(params[:id], self)
- respond_to do |format|
- format.xml { network_config.to_xml }
- format.json { network_config.to_json }
- end
- end
- end
-end
-end
-
-global_collection :network_templates do
-
- description 'Network Template is a set of configuration values for realizing a Network. An instance of Network Template may be used to create multiple Networks'
-
- operation :index do
- description 'List all Network Templates in the NetworkTemplateCollection'
- param :CIMISelect, :string, :optional
- control do
- network_templates = NetworkTemplateCollection.default(self).filter_by(params[:CIMISelect])
- respond_to do |format|
- format.xml {network_templates.to_xml}
- format.json {network_templates.to_json}
- end
- end
- end
-
- operation :show do
- description 'Show a specific Network Template'
- param :id, :string, :required
- control do
- network_template = NetworkTemplate.find(params[:id], self)
- respond_to do |format|
- format.xml {network_template.to_xml}
- format.json {network_template.to_json}
- end
- end
- end
-
-end
-
-
-global_collection :routing_groups do
-
- description 'Routing Groups represent a collection of Networks that route to each other. Providers shall not allow two Networks to be routable to each other unless they are explicitly connected by being part of a common RoutingGroup.'
-
- operation :index do
- description 'List all RoutingGroups in the RoutingGroupsCollection'
- param :CIMISelect, :string, :optional
- control do
- routing_groups = RoutingGroupCollection.default(self).filter_by(params[:CIMISelect])
- respond_to do |format|
- format.xml {routing_groups.to_xml}
- format.json {routing_groups.to_json}
- end
- end
- end
-
- operation :show do
- description 'Show a specific RoutingGroup'
- param :id, :string, :required
- control do
- routing_group = RoutingGroup.find(params[:id], self)
- respond_to do |format|
- format.xml {routing_group.to_xml}
- format.json {routing_group.to_json}
- end
- end
- end
-
-end
-
-
-global_collection :routing_group_templates do
-
- description 'Routing Groups Templates capture the configuration values for realizing a RoutingGroup. A Routing Group Template may be used to create multiple RoutingGroups'
-
- operation :index do
- description 'List all RoutingGroupTemplates in the RoutingGroupTemplateCollection'
- param :CIMISelect, :string, :optional
- control do
- routing_group_templates = RoutingGroupTemplateCollection.default(self).filter_by(params[:CIMISelect])
- respond_to do |format|
- format.xml {routing_group_templates.to_xml}
- format.json {routing_group_templates.to_json}
- end
- end
- end
-
- operation :show do
- description 'Show a specific RoutingGroupTemplate'
- param :id, :string, :required
- control do
- routing_group_template = RoutingGroupTemplate.find(params[:id], self)
- respond_to do |format|
- format.xml {routing_group_template.to_xml}
- format.json {routing_group_template.to_json}
- end
- end
- end
-
-end
-
-
-global_collection :vsps do
-
- description 'A VSP represents the connection parameters of a network port'
-
- operation :index do
- description 'List all VSPs in the VSPCollection'
- param :CIMISelect, :string, :optional
- control do
- vsps = VSPCollection.default(self).filter_by(params[:CIMISelect])
- respond_to do |format|
- format.xml {vsps.to_xml}
- format.json {vsps.to_json}
- end
- end
- end
-
- operation :show do
- description 'Show a specific VSP'
- param :id, :string, :required
- control do
- vsp = VSP.find(params[:id], self)
- respond_to do |format|
- format.xml {vsp.to_xml}
- format.json {vsp.to_json}
- end
- end
- end
-
- operation :create do
- description "Create a new VSP"
- control do
- if request.content_type.end_with?("json")
- vsp = CIMI::Model::VSP.create(request.body.read, self, :json)
- else
- vsp = CIMI::Model::VSP.create(request.body.read, self, :xml)
- end
- respond_to do |format|
- format.xml { vsp.to_xml }
- format.json { vsp.to_json }
- end
- end
- end
-
- operation :destroy do
- description "Delete a specified VSP"
- param :id, :string, :required
- control do
- CIMI::Model::VSP.delete!(params[:id], self)
- no_content_with_status(200)
- end
- end
-
- operation :start, :method => :post, :member => true do
- description "Start specific VSP."
- param :id, :string, :required
- control do
- vsp = VSP.find(params[:id], self)
- report_error(404) unless vsp
- if request.content_type.end_with?("json")
- action = Action.from_json(request.body.read)
- else
- action = Action.from_xml(request.body.read)
- end
- vsp.perform(action, self) do |operation|
- no_content_with_status(202) if operation.success?
- # Handle errors using operation.failure?
- end
- end
- end
-
- operation :stop, :method => :post, :member => true do
- description "Stop specific VSP."
- param :id, :string, :required
- control do
- vsp = VSP.find(params[:id], self)
- report_error(404) unless vsp
- if request.content_type.end_with?("json")
- action = Action.from_json(request.body.read)
- else
- action = Action.from_xml(request.body.read)
- end
- vsp.perform(action, self) do |operation|
- no_content_with_status(202) if operation.success?
- # Handle errors using operation.failure?
- end
- end
- end
-
-end
-
-global_collection :vsp_configurations do
-
- description 'A VSP Configuration is the set of configuration values representing the information needed to create a VSP with certain characteristics'
-
- operation :index do
- description 'List all VSPConfigurations in the VSPConfigurationCollection'
- param :CIMISelect, :string, :optional
- control do
- vsp_configs = VSPConfigurationCollection.default(self).filter_by(params[:CIMISelect])
- respond_to do |format|
- format.xml {vsp_configs.to_xml}
- format.json {vsp_configs.to_json}
- end
- end
- end
-
- operation :show do
- description 'Show a specific VSPConfiguration'
- param :id, :string, :required
- control do
- vsp_config = VSPConfiguration.find(params[:id], self)
- respond_to do |format|
- format.xml {vsp_config.to_xml}
- format.json {vsp_config.to_json}
- end
- end
- end
-
-end
-
-
-global_collection :vsp_templates do
-
- description 'The VSP Template is a set of Configuration values for realizing a VSP. A VSP Template may be used to create multiple VSPs'
-
- operation :index do
- description 'List all VSPTemplates in the VSPTemplateCollection'
- param :CIMISelect, :string, :optional
- control do
- vsp_templates = VSPTemplateCollection.default(self).filter_by(params[:CIMISelect])
- respond_to do |format|
- format.xml {vsp_templates.to_xml}
- format.json {vsp_templates.to_json}
- end
- end
- end
-
- operation :show do
- description 'Show a specific VSPTemplate'
- param :id, :string, :required
- control do
- vsp_template = VSPTemplate.find(params[:id], self)
- respond_to do |format|
- format.xml {vsp_template.to_xml}
- format.json {vsp_template.to_json}
- end
- end
- end
-
-end
-
-global_collection :addresses do
-
- description 'An Address represents an IP address, and its associated metdata, for a particular Network.'
-
- operation :index do
- description 'List all Addresses in the AddressCollection'
- param :CIMISelect, :string, :optional
- control do
- addresses = AddressCollection.default(self).filter_by(params[:CIMISelect])
- respond_to do |format|
- format.xml {addresses.to_xml}
- format.json {addresses.to_json}
- end
- end
- end
-
- operation :show do
- description 'Show a specific Address'
- param :id, :string, :required
- control do
- address = CIMI::Model::Address.find(params[:id], self)
- respond_to do |format|
- format.xml {address.to_xml}
- format.json {address.to_json}
- end
- end
- end
-
- operation :create do
- description "Create a new Address"
- control do
- if request.content_type.end_with?("json")
- address = CIMI::Model::Address.create(request.body.read, self, :json)
- else
- address = CIMI::Model::Address.create(request.body.read, self, :xml)
- end
- respond_to do |format|
- format.xml { address.to_xml }
- format.json { address.to_json }
- end
- end
- end
-
- operation :destroy do
- description "Delete a specified Address"
- param :id, :string, :required
- control do
- CIMI::Model::Address.delete!(params[:id], self)
- no_content_with_status(200)
- end
- end
-
-end
-
-
-global_collection :address_templates do
-
- description 'An AddressTemplate captures the configuration values for realizing an Address. An Address Template may be used to create multiple Addresses.'
-
- operation :index do
- description 'List all AddressTemplates in the AddressTemplateCollection'
- param :CIMISelect, :string, :optional
- control do
- address_templates = AddressTemplateCollection.default(self).filter_by(params[:CIMISelect])
- respond_to do |format|
- format.xml {address_templates.to_xml}
- format.json {address_templates.to_json}
- end
- end
- end
-
- operation :show do
- description 'Show a specific AddressTemplate'
- param :id, :string, :required
- control do
- address_template = CIMI::Model::AddressTemplate.find(params[:id], self)
- respond_to do |format|
- format.xml {address_template.to_xml}
- format.json {address_template.to_json}
- end
- end
- end
-
end
--
1.7.10.1
[PATCH core 06/26] Core: Added the :name property to Deltacloud Key model
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/views/keys/show.xml.haml | 1 +
1 file changed, 1 insertion(+)
diff --git a/server/views/keys/show.xml.haml b/server/views/keys/show.xml.haml
index abb513b..2e98d0d 100644
--- a/server/views/keys/show.xml.haml
+++ b/server/views/keys/show.xml.haml
@@ -1,6 +1,7 @@
- unless defined?(partial)
!!! XML
%key{ :href => key_url(@key.id), :id => @key.id, :type => "#{@key.credential_type}" }
+ %name=@key.name
%actions
- if driver.respond_to?(:destroy_key)
%link{ :rel => "destroy", :method => "delete", :href => destroy_key_url(@key.id)}
--
1.7.10.1
[PATCH core 16/26] Core: Fixed the respond_to extension to return XML when no media-type
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/lib/sinatra/rack_accept.rb | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/server/lib/sinatra/rack_accept.rb b/server/lib/sinatra/rack_accept.rb
index e4a0f1f..4286e93 100644
--- a/server/lib/sinatra/rack_accept.rb
+++ b/server/lib/sinatra/rack_accept.rb
@@ -79,9 +79,16 @@ module Rack
@media_type ||= accepting_formats.to_a.sort { |a,b| a[1]<=>b[1] }.reverse.select do |format, priority|
wants.keys.include?(format) == true
end.first
+ if @media_type and @media_type.kind_of? Symbol
+ @media_type = [ @media_type ]
+ end
if @media_type and @media_type[0]
@media_type = @media_type[0]
- headers 'Content-Type' => Rack::MediaType::ACCEPTED_MEDIA_TYPES[@media_type][:return]
+ if Rack::MediaType::ACCEPTED_MEDIA_TYPES[@media_type]
+ headers 'Content-Type' => Rack::MediaType::ACCEPTED_MEDIA_TYPES[@media_type][:return]
+ else
+ headers 'Content-Type' => 'application/xml'
+ end
wants[@media_type.to_sym].call if wants[@media_type.to_sym]
else
headers 'Content-Type' => nil
--
1.7.10.1
[PATCH core 07/26] Core: Added check if user_metadata in blob respond to the :each method
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/views/blobs/show.xml.haml | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/server/views/blobs/show.xml.haml b/server/views/blobs/show.xml.haml
index 34b9cca..bde9e80 100644
--- a/server/views/blobs/show.xml.haml
+++ b/server/views/blobs/show.xml.haml
@@ -1,11 +1,14 @@
!!! XML
%blob{:href => bucket_url(@blob.bucket) + '/' + @blob.id, :id => @blob.id}
+ %bucket{ :id => @blob.bucket, :href => bucket_url(@blob.bucket)}
- @blob.attributes.select{ |attr| (attr!=:id && attr!=:user_metadata) }.each do |attribute|
+ - next if attribute == :bucket
- unless attribute == :content
- haml_tag(attribute, :<) do
- haml_concat @blob.send(attribute)
%user_metadata
- - @blob.user_metadata.each do |k, v|
- %entry{:key => k}
- #{cdata v}
- %content{:href => bucket_url(@blob.bucket) + '/' + @blob.id + '/content'}
+ - if @blob.user_metadata.respond_to? :each
+ - @blob.user_metadata.each do |k, v|
+ %entry{:key => k}
+ #{cdata v}
+ %content{:href => bucket_url(@blob.bucket) + '/' + @blob.id + '/content', :rel => 'blob_content'}
--
1.7.10.1
[PATCH core 01/26] Core: Added sinatra-rabbit gem to gemspec
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/deltacloud-core.gemspec | 1 +
1 file changed, 1 insertion(+)
diff --git a/server/deltacloud-core.gemspec b/server/deltacloud-core.gemspec
index 55aa182..54d88cc 100644
--- a/server/deltacloud-core.gemspec
+++ b/server/deltacloud-core.gemspec
@@ -68,6 +68,7 @@ Gem::Specification.new do |s|
s.add_dependency('rake', '>= 0.8.7')
s.add_dependency('haml', '>= 2.2.17')
s.add_dependency('sinatra', '>= 0.9.4')
+ s.add_dependency('sinatra-rabbit', '>= 1.0.5')
s.add_dependency('rack', '>= 1.0.0')
s.add_dependency('rack-accept')
s.add_dependency('json', '>= 1.1.9')
--
1.7.10.1
[PATCH core 09/26] Core: Updated entrypoint views to list collections from Deltacloud module
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/views/api/show.html.haml | 7 +++----
server/views/api/show.xml.haml | 13 ++++++-------
2 files changed, 9 insertions(+), 11 deletions(-)
diff --git a/server/views/api/show.html.haml b/server/views/api/show.html.haml
index 9a40860..4630a87 100644
--- a/server/views/api/show.html.haml
+++ b/server/views/api/show.html.haml
@@ -3,20 +3,19 @@
%div{ :'data-role' => :content, :'data-theme' => 'c'}
%ul{ :'data-role' => :listview, :'data-inset' => 'true'}
-
- Deltacloud.collections.each do |c|
- next unless c.operation(:index).has_capability?
%li
%a{ :href => api_url_for(c.collection_name), :'data-icon' => "arrow-r"}=c.collection_name.to_s.gsub('_', ' ').titlecase
-- if @providers.size > 1
+- unless driver.configured_providers.empty?
%div{ :'data-role' => :footer, :'data-theme' => 'a'}
%form{ :action => settings.root_url, :method => :post, :'data-ajax' => 'false'}
- if @driver_name
- %input{ :name => :driver, :type => :hidden, :value => @driver_name }/
+ %input{ :name => :driver, :type => :hidden, :value => driver.name }/
%fieldset{ :'data-type' => "horizontal", :'data-role' => "controlgroup", :id => "providers" }
%select{:name => 'provider', :'data-inline' => 'true', :'data-native-menu' => 'false'}
%option{ :value => 'default'} default
- - @providers.each do |provider|
+ - driver.configured_providers.each do |provider|
%option{ :value => provider } #{provider}
%button{ :type => :submit, :'data-inline' => "true"} Change Provider
diff --git a/server/views/api/show.xml.haml b/server/views/api/show.xml.haml
index 2b9f653..ad2df09 100644
--- a/server/views/api/show.xml.haml
+++ b/server/views/api/show.xml.haml
@@ -1,7 +1,6 @@
-%api{ :version => settings.version, :driver => driver_symbol }
- - for entry_point in entry_points
- %link{ :rel=>entry_point[0], :href=>entry_point[1] }
- - for feature in driver.features(entry_point[0])
- %feature{ :name=>feature.name }
- - for name,value in feature.constraints
- %constraint{ :name=>name, :value=>value }
+%api{ :version => settings.version, :driver => driver_symbol, :provider => Thread.current[:provider] || ENV['API_PROVIDER'] }
+ - Deltacloud.collections.each do |c|
+ - next unless c.operation(:index).has_capability?
+ %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|
+ %feature{ :name => f.name }
--
1.7.10.1
[PATCH core 25/26] SBC: Fixed cucumber scenatios to work with Sinatra::Base
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
tests/sbc/step_definitions/common_steps.rb | 2 +
tests/sbc/support/env.rb | 82 ++++++++++++----------------
2 files changed, 36 insertions(+), 48 deletions(-)
diff --git a/tests/sbc/step_definitions/common_steps.rb b/tests/sbc/step_definitions/common_steps.rb
index a9cf8e4..d5f9c15 100644
--- a/tests/sbc/step_definitions/common_steps.rb
+++ b/tests/sbc/step_definitions/common_steps.rb
@@ -1,3 +1,5 @@
+World(Rack::Test::Methods)
+
Given /^I enter ([A-Za-z_]+) collection$/ do |collection|
@current_collection = collection
@current_collection_url = "/api/%s" % collection.strip
diff --git a/tests/sbc/support/env.rb b/tests/sbc/support/env.rb
index 2ca8cb4..fe27329 100644
--- a/tests/sbc/support/env.rb
+++ b/tests/sbc/support/env.rb
@@ -1,67 +1,53 @@
-SERVER_DIR = File::expand_path(File::join(File::dirname(__FILE__), "../../../server"))
+require 'rubygems'
+require 'nokogiri'
+SERVER_DIR = File::expand_path(File::join(File::dirname(__FILE__), "../../../server"))
$top_srcdir = SERVER_DIR
$:.unshift File::join($top_srcdir, 'lib')
-
-ENV['API_DRIVER'] = 'sbc'
-
Dir.chdir(SERVER_DIR)
-require 'rubygems'
-require 'nokogiri'
-require 'deltacloud/server'
-require 'rack/test'
-
-Sinatra::Application.set :environment, :test
-Sinatra::Application.set :root, SERVER_DIR
-
-CONFIG = {
- :username => 'sbc_test_username',
- :password => 'sbc_test_password'
-}
-
-ENV['RACK_ENV'] = 'test'
+API_VERSION = "9.9.9"
+API_ROOT_URL = "/api"
+ENV['API_DRIVER'] = 'sbc'
+ENV.delete('API_VERBOSE')
-World do
+load File.join($top_srcdir, 'lib', 'deltacloud', 'server.rb')
- include Rack::Test::Methods
+require 'rack/test'
- module Rack
- module Test
- class Session
- def headers
- @headers
- end
- end
- end
- class MockSession
- def set_last_response(response)
- @last_response = response
+module Rack
+ module Test
+ class Session
+ def headers
+ @headers
end
end
end
-
- def app
- @app = Rack::Builder.new do
- set :environment => :development
- set :loggining => true
- set :raise_errors => true
- set :show_exceptions => true
- run Sinatra::Application
+ class MockSession
+ def set_last_response(response)
+ @last_response = response
end
end
+end
- def xml
- Nokogiri::XML(last_response.body)
- end
-
+CONFIG = {
+ :username => 'mockuser',
+ :password => 'mockpassword'
+}
+def output_xml
+ Nokogiri::XML(last_response.body)
+end
- Before do
- unless @no_header
- header 'Accept', 'application/xml;q=9'
- end
- end
+def xml
+ Nokogiri::XML(last_response.body)
+end
+def app
+ Rack::URLMap.new(
+ "/" => Deltacloud::API.new,
+ "/stylesheets" => Rack::Directory.new( "public/stylesheets" ),
+ "/javascripts" => Rack::Directory.new( "public/javascripts" )
+ )
end
--
1.7.10.1
[PATCH core 22/26] Tests: Fixed mock Cucumber tests to work for Sinatra::Base
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/lib/deltacloud/helpers/deltacloud_helper.rb | 5 +++
server/lib/deltacloud/helpers/url_helper.rb | 4 +--
server/lib/deltacloud/models/instance.rb | 4 +++
server/views/instances/show.xml.haml | 4 +--
tests/ec2/api.feature | 3 +-
tests/ec2/step_definitions/api_steps.rb | 37 ++++++++++----------
tests/ec2/support/env.rb | 31 ++++++++--------
tests/mock/api.feature | 1 -
tests/mock/images.feature | 2 ++
tests/mock/step_definitions/images_steps.rb | 14 ++++----
tests/mock/storage_snapshots.feature | 1 +
tests/mock/support/env.rb | 11 +++++-
12 files changed, 68 insertions(+), 49 deletions(-)
diff --git a/server/lib/deltacloud/helpers/deltacloud_helper.rb b/server/lib/deltacloud/helpers/deltacloud_helper.rb
index ee9decc..2fc2a59 100644
--- a/server/lib/deltacloud/helpers/deltacloud_helper.rb
+++ b/server/lib/deltacloud/helpers/deltacloud_helper.rb
@@ -26,6 +26,11 @@ module Deltacloud::Helpers
end
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)
+ end
+
def instance_action_method(action)
action_method(action, Sinatra::Rabbit::InstancesCollection)
end
diff --git a/server/lib/deltacloud/helpers/url_helper.rb b/server/lib/deltacloud/helpers/url_helper.rb
index fd71f43..b35c714 100644
--- a/server/lib/deltacloud/helpers/url_helper.rb
+++ b/server/lib/deltacloud/helpers/url_helper.rb
@@ -79,7 +79,7 @@ module Sinatra
def url_for url_fragment, mode=:path_only
case mode
when :path_only
- base = request.script_name
+ base = request.script_name.empty? ? API_ROOT_URL : request.script_name
when :full
scheme = request.scheme
port = request.port
@@ -96,7 +96,7 @@ module Sinatra
else
port = ":#{port}"
end
- base = "#{scheme}://#{request_host}#{port}#{request.script_name}"
+ base = "#{scheme}://#{request_host}#{port}#{request.script_name.empty? ? API_ROOT_URL : request.script_name}"
else
raise TypeError, "Unknown url_for mode #{mode}"
end
diff --git a/server/lib/deltacloud/models/instance.rb b/server/lib/deltacloud/models/instance.rb
index c2cc0e3..9db5b4b 100644
--- a/server/lib/deltacloud/models/instance.rb
+++ b/server/lib/deltacloud/models/instance.rb
@@ -34,6 +34,10 @@ class Instance < BaseModel
attr_accessor :firewalls
attr_accessor :storage_volumes
+ def storage_volumes
+ @storage_volumes || []
+ end
+
def can_create_image?
self.create_image
end
diff --git a/server/views/instances/show.xml.haml b/server/views/instances/show.xml.haml
index 3a62ed3..a91c0c5 100644
--- a/server/views/instances/show.xml.haml
+++ b/server/views/instances/show.xml.haml
@@ -45,8 +45,8 @@
%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?(:authentication_key) or driver.class.has_feature?(:authentication_password)
- %authentication{ :type => driver_auth_feature_name }
+ - if driver.class.has_feature?(:instances, :authentication_key) or driver.class.has_feature?(:instances, :authentication_password)
+ %authentication{ :type => auth_feature_name }
- if @instance.authn_feature_failed?
%error #{@instance.authn_error}
- else
diff --git a/tests/ec2/api.feature b/tests/ec2/api.feature
index 2dcd1b2..9d53722 100644
--- a/tests/ec2/api.feature
+++ b/tests/ec2/api.feature
@@ -26,6 +26,7 @@ Feature: Accessing API entry points
| storage_volumes |
| addresses |
| firewalls |
+ | metrics |
And this URI should be available in XML, JSON, HTML format
Scenario: Following entry points
@@ -46,6 +47,7 @@ Feature: Accessing API entry points
| storage_volumes |
| addresses |
| firewalls |
+ | metrics |
And each link should have 'rel' attribute with valid name
And each link should have 'href' attribute with valid URL
When client follow this attribute
@@ -61,4 +63,3 @@ Feature: Accessing API entry points
| user_data |
| instance_count |
| firewalls |
- | hardware_profiles |
diff --git a/tests/ec2/step_definitions/api_steps.rb b/tests/ec2/step_definitions/api_steps.rb
index c42af6f..27172e8 100644
--- a/tests/ec2/step_definitions/api_steps.rb
+++ b/tests/ec2/step_definitions/api_steps.rb
@@ -8,11 +8,11 @@ end
Given /^URI ([\w\/\-_]+) exists in (.+) format$/ do |uri, format|
@uri = uri
case format.downcase
- when 'xml':
+ when 'xml' then
header 'Accept', 'application/xml;q=9'
- when 'json'
+ when 'json' then
header 'Accept', 'application/json;q=9'
- when 'html'
+ when 'html' then
header 'Accept', 'application/xml+xhtml;q=9'
end
get @uri, {}
@@ -52,11 +52,11 @@ Then /^this URI should be available in (.+) format$/ do |formats|
@no_header = true
formats.split(',').each do |format|
case format.downcase
- when 'xml':
+ when 'xml' then
header 'Accept', 'application/xml;q=9'
- when 'json'
+ when 'json' then
header 'Accept', 'application/json;q=9'
- when 'html'
+ when 'html' then
header 'Accept', 'application/xml+xhtml;q=9'
end
get @uri, {}
@@ -75,15 +75,15 @@ end
Then /^each (\w+) should have '(.+)' attribute with valid (.+)$/ do |el, attr, t|
case el
- when 'link':
+ when 'link' then
path = '/api/link'
- when 'image':
+ when 'image' then
path = '/images/image'
- when 'instance':
+ when 'instance' then
path = '/instances/instance'
- when 'key':
+ when 'key' then
path = '/keys/key'
- when 'realm':
+ when 'realm' then
path = '/realms/realm'
end
output_xml.xpath(path).each do |entry_point|
@@ -97,11 +97,11 @@ end
Then /^each ([\w\-]+) should have '(.+)' attribute set to '(.+)'$/ do |el, attr, v|
case el
- when 'image':
+ when 'image' then
path = "/images/image"
- when 'hardware_profile':
+ when 'hardware_profile' then
path = "/hardware_profiles/hardware_profile"
- when 'instance':
+ when 'instance' then
path = "/instances/instance"
end
output_xml.xpath(path).each do |element|
@@ -111,11 +111,11 @@ end
Then /^each ([\w\-]+) should have '(.+)' element set to '(.+)'$/ do |el, child, v|
case el
- when 'image':
+ when 'image' then
path = "/images/image"
- when 'hardware_profile':
+ when 'hardware_profile' then
path = "/hardware_profiles/hardware_profile"
- when 'instance':
+ when 'instance' then
path = "/instances/instance"
end
output_xml.xpath(path).each do |element|
@@ -126,7 +126,7 @@ end
Then /^each ([\w\-]+) should have '(.+)' property set to '(.+)'$/ do |el, property, v|
case el
- when 'hardware_profile':
+ when 'hardware_profile' then
path = "/hardware_profiles/hardware_profile"
end
output_xml.xpath(path).each do |element|
@@ -143,6 +143,7 @@ When /^client follow this attribute$/ do
end
Then /^client should get a valid response$/ do
+ puts last_response.body
last_response.status.should_not == 500
end
diff --git a/tests/ec2/support/env.rb b/tests/ec2/support/env.rb
index 5245bb1..5eb006a 100644
--- a/tests/ec2/support/env.rb
+++ b/tests/ec2/support/env.rb
@@ -1,21 +1,20 @@
-SERVER_DIR = File::expand_path(File::join(File::dirname(__FILE__), "../../../server"))
+require 'rubygems'
+require 'nokogiri'
+SERVER_DIR = File::expand_path(File::join(File::dirname(__FILE__), "../../../server"))
$top_srcdir = SERVER_DIR
$:.unshift File::join($top_srcdir, 'lib')
-
Dir.chdir(SERVER_DIR)
-ENV['API_DRIVER'] = 'ec2'
+API_VERSION = "9.9.9"
+API_ROOT_URL = "/api"
-require 'rubygems'
-require 'nokogiri'
-require 'deltacloud/server'
-require 'rack/test'
+ENV['API_DRIVER'] = 'ec2'
+ENV.delete('API_VERBOSE')
-Sinatra::Application.set :environment, :test
-Sinatra::Application.set :root, SERVER_DIR
+load File.join($top_srcdir, 'lib', 'deltacloud', 'server.rb')
-require '../server/lib/deltacloud/base_driver/mock_driver'
+require 'rack/test'
CONFIG = {
:username => 'mockuser',
@@ -26,13 +25,11 @@ World do
include Rack::Test::Methods
def app
- @app = Rack::Builder.new do
- set :environment => :test
- set :loggining => true
- set :raise_errors => true
- set :show_exceptions => false
- run Sinatra::Application
- end
+ @app = Rack::URLMap.new(
+ "/" => Deltacloud::API.new,
+ "/stylesheets" => Rack::Directory.new( "public/stylesheets" ),
+ "/javascripts" => Rack::Directory.new( "public/javascripts" )
+ )
end
def output_xml
diff --git a/tests/mock/api.feature b/tests/mock/api.feature
index d7fb044..59d00d1 100644
--- a/tests/mock/api.feature
+++ b/tests/mock/api.feature
@@ -53,4 +53,3 @@ Feature: Accessing API entry points
| user_name |
| user_data |
| authentication_key |
- | hardware_profiles |
diff --git a/tests/mock/images.feature b/tests/mock/images.feature
index 0bae93d..0094548 100644
--- a/tests/mock/images.feature
+++ b/tests/mock/images.feature
@@ -12,6 +12,7 @@ Feature: Listing and showing images
| architecture |
| owner_id |
| state |
+ | hardware_profiles |
| actions |
And each image should have 'href' attribute with valid URL
And this URI should be available in XML, JSON, HTML format
@@ -29,6 +30,7 @@ Feature: Listing and showing images
| name |
| description |
| architecture |
+ | hardware_profiles |
| owner_id |
| state |
| actions |
diff --git a/tests/mock/step_definitions/images_steps.rb b/tests/mock/step_definitions/images_steps.rb
index 323778c..46963bb 100644
--- a/tests/mock/step_definitions/images_steps.rb
+++ b/tests/mock/step_definitions/images_steps.rb
@@ -27,17 +27,17 @@ end
When /^client want to show first (.+)$/ do |element|
case element
- when 'image':
+ when 'image' then
path = '/images/image'
- when 'instance':
+ when 'instance' then
path = '/instances/instance'
- when 'realm':
+ when 'realm' then
path = '/realms/realm'
- when 'hardware_profile'
+ when 'hardware_profile' then
path = '/hardware_profiles/hardware_profile'
- when 'storage_volume':
+ when 'storage_volume' then
path = '/storage_volumes/storage_volume'
- when 'storage_snapshot':
+ when 'storage_snapshot' then
path = '/storage_snapshots/storage_snapshot'
end
@element = output_xml.xpath(path).first
@@ -58,7 +58,7 @@ Then /^client follow (\w+) attribute in first (.+)$/ do |attr, el|
end
Then /^client should get this (.+)$/ do |el|
- last_response.status.should == 200
+ last_response.status.to_s =~ /2(\d{2})/
end
diff --git a/tests/mock/storage_snapshots.feature b/tests/mock/storage_snapshots.feature
index fdaff9c..350a2a3 100644
--- a/tests/mock/storage_snapshots.feature
+++ b/tests/mock/storage_snapshots.feature
@@ -23,4 +23,5 @@ Feature: Accessing storage snapshots
Then client should get this storage_snapshot
And this storage_snapshot should have:
| created |
+ | name |
| storage_volume |
diff --git a/tests/mock/support/env.rb b/tests/mock/support/env.rb
index 5f97b89..a961af2 100644
--- a/tests/mock/support/env.rb
+++ b/tests/mock/support/env.rb
@@ -6,8 +6,12 @@ $top_srcdir = SERVER_DIR
$:.unshift File::join($top_srcdir, 'lib')
Dir.chdir(SERVER_DIR)
+API_VERSION = "9.9.9"
+API_ROOT_URL = "/api"
+
ENV['API_DRIVER'] = 'mock'
ENV.delete('API_VERBOSE')
+
load File.join($top_srcdir, 'lib', 'deltacloud', 'server.rb')
require 'rack/test'
@@ -21,6 +25,11 @@ def output_xml
Nokogiri::XML(last_response.body)
end
+
def app
- Sinatra::Application
+ Rack::URLMap.new(
+ "/" => Deltacloud::API.new,
+ "/stylesheets" => Rack::Directory.new( "public/stylesheets" ),
+ "/javascripts" => Rack::Directory.new( "public/javascripts" )
+ )
end
--
1.7.10.1
[PATCH core 14/26] Core: Added possiblity to list all Rabbit defined routes by rake rabbit:routes
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/Rakefile | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
server/config.ru | 40 +++++++++++++++++-----------------------
2 files changed, 68 insertions(+), 23 deletions(-)
diff --git a/server/Rakefile b/server/Rakefile
index 147cdcf..5d205ce 100644
--- a/server/Rakefile
+++ b/server/Rakefile
@@ -193,3 +193,54 @@ namespace :mock do
end
end
+
+namespace :openshift do
+
+ desc "Deploy Deltacloud API to OpenShift"
+ task :deploy do
+ print "RHN or OpenShift login with OpenShift Express access: "
+ STDOUT.flush
+ login = STDIN.gets.chomp
+ print "Password: "
+ system "stty -echo"
+ password = STDIN.gets.chomp
+ system "stty echo"
+ puts `rhc-create-app --rhlogin #{login} --app deltacloudtest --password '#{password}' --type ruby-1.8`
+ end
+end
+
+namespace :rabbit do
+ load File.join(File.dirname(__FILE__), 'config.ru')
+
+ desc "List the routes defined by Rabbit"
+ task :routes do
+ Deltacloud.collections.each do |c|
+ puts "\033[1;32;m#{c.name}\33[0m"
+ c.operations.each do |o|
+ puts "\033[1;37m%6s\033[0m :%-10s %-30s (%s)" % [
+ o.http_method.to_s.upcase,
+ o.operation_name,
+ o.full_path,
+ o.description[0..100]
+ ]
+ end
+ unless c.collections.empty?
+ puts
+ c.collections.each do |s|
+ puts "\033[1;32;m#{s.name}\33[0m"
+ s.operations.each do |o|
+ puts "\033[1;37m%6s\033[0m :%-10s %-30s (%s)" % [
+ o.http_method.to_s.upcase,
+ o.operation_name,
+ o.full_path,
+ o.description[0..100]
+ ]
+ end
+ end
+ end
+ puts
+ end
+ end
+
+end
+
diff --git a/server/config.ru b/server/config.ru
index f17de43..2280d0e 100644
--- a/server/config.ru
+++ b/server/config.ru
@@ -22,6 +22,7 @@ ENV['API_DRIVER'] ||= 'mock'
# Set the API frontend use ('cimi' or 'deltacloud', default is 'deltacloud')
ENV['API_FRONTEND'] ||= 'deltacloud'
+
# We need to set different API version and entrypoint location
if ENV['API_FRONTEND'] == 'deltacloud'
API_VERSION = "9.9.9"
@@ -31,29 +32,22 @@ elsif ENV['API_FRONTEND'] == 'cimi'
API_ROOT_URL = "/cloudEntryPoint"
end
-#begin
- require File.join(File.dirname(__FILE__), 'lib', ENV['API_FRONTEND'], 'server.rb')
-#rescue LoadError => e
-# puts "[ERROR] The specified frontend (#{ENV['API_FRONTEND']}) not supported (#{e.message})"
-# exit 1
-#end
-
-if self.respond_to? :map
- map "/" do
- class IndexEntrypoint < Sinatra::Base
- get "/" do
- redirect API_ROOT_URL, 301
- end
- end
- run IndexEntrypoint
- end
+require File.join(File.dirname(__FILE__), 'lib', ENV['API_FRONTEND'], 'server.rb')
- map API_ROOT_URL do
- use Rack::Static, :urls => ["/stylesheets", "/javascripts"], :root => "public"
- use Rack::Session::Cookie
- case ENV['API_FRONTEND']
- when 'deltacloud' then run Rack::Cascade.new([Deltacloud::API])
- when 'cimi' then run Rack::Cascade.new([CIMI::API])
- end
+frontend_klass = case ENV['API_FRONTEND']
+ when 'deltacloud' then Deltacloud::API
+ when 'cimi' then CIMI::API
+end
+
+class IndexEntrypoint < Sinatra::Base
+ get "/" do
+ redirect API_ROOT_URL, 301
end
end
+
+run Rack::URLMap.new(
+ "/" => IndexEntrypoint.new,
+ API_ROOT_URL => frontend_klass.new,
+ "/stylesheets" => Rack::Directory.new( "public/stylesheets" ),
+ "/javascripts" => Rack::Directory.new( "public/javascripts" )
+) if respond_to? :run
--
1.7.10.1
[PATCH core 19/26] Core: Fixed wrong brackets in response_to block under load_balancers
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/lib/deltacloud/collections/load_balancers.rb | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/server/lib/deltacloud/collections/load_balancers.rb b/server/lib/deltacloud/collections/load_balancers.rb
index d093ead..f6fbc62 100644
--- a/server/lib/deltacloud/collections/load_balancers.rb
+++ b/server/lib/deltacloud/collections/load_balancers.rb
@@ -48,7 +48,7 @@ module Deltacloud::Collections
@load_balancer = driver.load_balancer(credential, params[:id])
respond_to do |format|
format.xml { haml :'load_balancers/show' }
- format.json ( xml_to_json('load_balancers/show'))
+ format.json { xml_to_json('load_balancers/show') }
format.html { haml :'load_balancers/show' }
end
end
@@ -61,7 +61,7 @@ module Deltacloud::Collections
@load_balancer = driver.load_balancer(credential, params[:id])
respond_to do |format|
format.xml { haml :'load_balancers/show' }
- format.json ( xml_to_json('load_balancers/show'))
+ format.json { xml_to_json('load_balancers/show')}
format.html { haml :'load_balancers/show' }
end
end
--
1.7.10.1
Re: [PATCH core 17/26] Core: Replaced Test/Unit based tests for Mock
with new minitest
Posted by Michal Fojtik <mf...@redhat.com>.
On 05/17/12, David Lutterkort wrote:
> On Tue, 2012-05-15 at 17:15 +0200, mfojtik@redhat.com wrote:
> > From: Michal Fojtik <mf...@redhat.com>
> >
> >
> > Signed-off-by: Michal fojtik <mf...@redhat.com>
>
> Running 'test:rackspace' fails for me right now, because there is no
> tests/drivers/rackspace/common.rb - did you forget to check that in ?
Yes, it's now on place, howerver there are some buckets related failures,
which I think are just expired fixtures that we need to re-record.
I also reworked all tests in the way where you should be able to use the
Ruby interpreter to run each test separately (ruby
tests/drivers/mock/api_test.rb, etc...)
So now **all** 'rake test:*' should be fixed/converted to work with the
Sinatra::Base version.
For example the openstack tests should run without a single failure.
There are some failures for the rackspace driver and for the rhevm
driver, but those are just expired fixtures. I'm going to re-record
them today/tomorrow.
After that we should have *all* tests fixed and ready for the QA to pick
them up.
-- Michal
>
> > diff --git a/server/Rakefile b/server/Rakefile
> > index 5d205ce..50971f5 100644
> > --- a/server/Rakefile
> > +++ b/server/Rakefile
> > @@ -244,3 +224,25 @@ namespace :rabbit do
> >
> > end
> >
> > +namespace :test do
> > +
> > + %w(mock rackspace rhevm openstack google fgcp).each do |driver|
> > + desc "Run #{driver} unit tests"
> > + Rake::TestTask.new(driver) { |t|
> > + Rake::Task["mock:fixtures:reset"].invoke
> > + t.test_files = ['tests/drivers/'+driver+'/common.rb'] + FileList.new("tests/drivers/#{driver}/*_test.rb")
>
> I generally like when you can run tests without rake, e.g. run 'ruby
> tests/drivers/mock/api_test.rb' Relying on the Rakefile to preload
> certain files makes that impossible. It would be better to structure
> tests so that they all have a "require_relative 'common'" which in turn
> requires tests/common.
>
> Also, I am curious about setup.rb: shouldn't that go into some sort of
> hook that is run at certain points in the test cycle (before any tests
> are run or before each suite etc.)
>
> > + t.options = "-v -v"
>
> Can we get rid of that ? It leads to a ton of spam on the console.
>
> David
>
>
--
Michal Fojtik
Sr. Software Engineer, Deltacloud API (http://deltacloud.org)
Re: [PATCH core 17/26] Core: Replaced Test/Unit based tests for
Mock with new minitest
Posted by David Lutterkort <lu...@redhat.com>.
On Tue, 2012-05-15 at 17:15 +0200, mfojtik@redhat.com wrote:
> From: Michal Fojtik <mf...@redhat.com>
>
>
> Signed-off-by: Michal fojtik <mf...@redhat.com>
Running 'test:rackspace' fails for me right now, because there is no
tests/drivers/rackspace/common.rb - did you forget to check that in ?
> diff --git a/server/Rakefile b/server/Rakefile
> index 5d205ce..50971f5 100644
> --- a/server/Rakefile
> +++ b/server/Rakefile
> @@ -244,3 +224,25 @@ namespace :rabbit do
>
> end
>
> +namespace :test do
> +
> + %w(mock rackspace rhevm openstack google fgcp).each do |driver|
> + desc "Run #{driver} unit tests"
> + Rake::TestTask.new(driver) { |t|
> + Rake::Task["mock:fixtures:reset"].invoke
> + t.test_files = ['tests/drivers/'+driver+'/common.rb'] + FileList.new("tests/drivers/#{driver}/*_test.rb")
I generally like when you can run tests without rake, e.g. run 'ruby
tests/drivers/mock/api_test.rb' Relying on the Rakefile to preload
certain files makes that impossible. It would be better to structure
tests so that they all have a "require_relative 'common'" which in turn
requires tests/common.
Also, I am curious about setup.rb: shouldn't that go into some sort of
hook that is run at certain points in the test cycle (before any tests
are run or before each suite etc.)
> + t.options = "-v -v"
Can we get rid of that ? It leads to a ton of spam on the console.
David
[PATCH core 17/26] Core: Replaced Test/Unit based tests for Mock with new minitest
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/Rakefile | 42 +-
.../deltacloud/drivers/mock/data/keys/test-key.yml | 28 +
server/tests/drivers/mock/api_test.rb | 210 ++++----
server/tests/drivers/mock/buckets_test.rb | 192 +++++++
server/tests/drivers/mock/common.rb | 58 +++
server/tests/drivers/mock/drivers_test.rb | 120 +++++
.../tests/drivers/mock/hardware_profiles_test.rb | 297 +++++++----
server/tests/drivers/mock/images_test.rb | 268 ++++++----
server/tests/drivers/mock/instance_states_test.rb | 67 ---
server/tests/drivers/mock/instances_test.rb | 545 ++++++++++++--------
server/tests/drivers/mock/keys_test.rb | 158 ++++++
server/tests/drivers/mock/realms_test.rb | 180 ++++---
server/tests/drivers/mock/setup.rb | 3 -
.../tests/drivers/mock/storage_snapshots_test.rb | 111 ++++
server/tests/drivers/mock/storage_volumes_test.rb | 119 +++++
server/tests/drivers/mock/url_for_test.rb | 67 ---
16 files changed, 1684 insertions(+), 781 deletions(-)
create mode 100644 server/lib/deltacloud/drivers/mock/data/keys/test-key.yml
create mode 100644 server/tests/drivers/mock/buckets_test.rb
create mode 100644 server/tests/drivers/mock/common.rb
create mode 100644 server/tests/drivers/mock/drivers_test.rb
delete mode 100644 server/tests/drivers/mock/instance_states_test.rb
create mode 100644 server/tests/drivers/mock/keys_test.rb
delete mode 100644 server/tests/drivers/mock/setup.rb
create mode 100644 server/tests/drivers/mock/storage_snapshots_test.rb
create mode 100644 server/tests/drivers/mock/storage_volumes_test.rb
delete mode 100644 server/tests/drivers/mock/url_for_test.rb
diff --git a/server/Rakefile b/server/Rakefile
index 5d205ce..50971f5 100644
--- a/server/Rakefile
+++ b/server/Rakefile
@@ -71,26 +71,6 @@ begin
rescue LoadError
end
-namespace :test do
- %w(mock rackspace rhevm openstack google fgcp).each do |driver|
- desc "Run #{driver} unit tests"
- Rake::TestTask.new(driver) { |t|
- t.test_files = ['tests/common.rb', "tests/drivers/#{driver}/setup.rb"] + FileList.new("tests/drivers/#{driver}/*_test.rb") + FileList.new('tests/rabbit_test.rb')
- t.options = "-v -v"
- t.verbose = true
- t.warning = false
- }
- end
-
- desc "Run CIMI frontend tests"
- Rake::TestTask.new "cimi" do |t|
- t.test_files = ["tests/cimi/cimi.rb", "tests/cimi/common/*_test.rb"]
- t.options = "-v -v"
- t.verbose = true
- t.warning = false
- end
-
-end
desc "Call our Test::Unit suite"
task :test do
@@ -244,3 +224,25 @@ namespace :rabbit do
end
+namespace :test do
+
+ %w(mock rackspace rhevm openstack google fgcp).each do |driver|
+ desc "Run #{driver} unit tests"
+ Rake::TestTask.new(driver) { |t|
+ Rake::Task["mock:fixtures:reset"].invoke
+ t.test_files = ['tests/drivers/'+driver+'/common.rb'] + FileList.new("tests/drivers/#{driver}/*_test.rb")
+ t.options = "-v -v"
+ t.verbose = true
+ t.warning = false
+ }
+ end
+
+ desc "Run CIMI frontend tests"
+ Rake::TestTask.new "cimi" do |t|
+ t.test_files = ["tests/cimi/cimi.rb", "tests/cimi/common/*_test.rb"]
+ t.options = "-v -v"
+ t.verbose = true
+ t.warning = false
+ end
+
+end
diff --git a/server/lib/deltacloud/drivers/mock/data/keys/test-key.yml b/server/lib/deltacloud/drivers/mock/data/keys/test-key.yml
new file mode 100644
index 0000000..a643917
--- /dev/null
+++ b/server/lib/deltacloud/drivers/mock/data/keys/test-key.yml
@@ -0,0 +1,28 @@
+---
+:credential_type: :key
+:fingerprint: 5e:ce:b6:dc:59:3b:5c:93:f8:2e:9d:20:ce:60:ca:f5:0b:8a:66:93
+:pem_rsa_key: |-
+ -----BEGIN RSA PRIVATE KEY-----
+ P9mRXOY7p2SmMzTGA6dwKxUp1NB8LNCIJ7sMGgAljsf=ToAi9qn9myx0EQJkE8FZ8FigUIMHS/T
+ 8EwP7Ayjztb8dczbC6sb/Ep2UWcegNUVHimyHstaEaO/3dCaFwLJ/kw=laAfLQAVj4sIr8EHDTg
+ /BFkgmwTAYlS/ybkEfO9J7AJlY6/agwYzDWp+VGAD9rMsl2EkkbkWdoTX4Aob9RqyHaFi2m1AAw
+ 2nhhqYpa1W4H=PJvyBcsXT3JynowSI8rTvo41oVwgSzv7YofGP0yV7BePm5pXZUUP2ZMByxbAUv
+ jvYRN/cMHbC6RW1ezR3uehCKdKFRXLTkoivoGj4ugrKgOwQP0HWI2orx/NW+6vYBxyCKiTJPZcK
+ x4BlRrlgvPST/7eaFv7/5Pqc3jWcp+bRC0qyYqQT9iq3gGNoc4ABFTI7zCeZ3p9tK8oje5fWo5m
+ 54P32hVGeBjfqT/MrEYbY5gbJU6LejCj7x6Ozlp4iHQtrYNhiZ0iP0W3nRhVFQHamKx9aoBXyeg
+ LLGxBOr+TfaeeBXRkXiaMuWoyPSzUQwWmaJhm0sjHf7e/iKiUggZkOHQ/eF9MWI4M+4wvyepfS0
+ 5vl2Ql/2rXv+Mx+c4cx1fjBhRrMPcGKmHGjNMjPyamTrlqueFRJYP45AYABP2U2AsNxoPfEG0qu
+ ki3DJOeC5x/03nODd=hQLzfdiQ3Yyt0GMw1EQN96cPaRtnjr3U4/ngxt0Fi6o7Z8E2+Uh5t4n8D
+ h0exXCOlOi9BDsJJz677mga/=5Sin/4Cw8=D8O1FHrWoA4ZQbWFE71F=/29PM90RHJf2bjgk2WF
+ piltKwVfGAxPOTcpmf=J+V3NHgT/EawMPHuEmwgNvx6smDBUgJaw0QYX/XG5xuiQ7HTkffJN6Cm
+ 6D4WCJPZUvO1r+v=T9v7Qu4j9ue/l2WwVZuvQsVD67jpzq2R72EHna6rcwwyMcdAlwikP9nzJIL
+ Ale7hQAWHIEeAvAxtwxEMSfTkuLQcD=i0ORysmInDxdORw4ue2YThj2Id/jmUy6IiEqMYeVpiRq
+ 6spq2ukt=+HHn6aBcYWbsD=e8/wOk0X0=ixZ0HF+xqYgsiiAk==rA4QEgrf+5djbIRZk1wegeIO
+ po/HZdF4qk32cKBjrrel2AzxfZeGxWNX7ObAE4HACXi3eSdcnm1fIHsoSC+1eDqFkfAIve3Dj/a
+ afZxrda6zzp3g6IPcHAqleCn7XNcS0v5tk4Fag8Wr5Wq7IipRfixAs+GESGiyugeRvZWN2mtDOL
+ CGHGGAbpvplw2vjdryVyj7P6bVcwLNgl0t1ufZBaGRBpyontJ1/UQQMew7e2lW=EZr/GxHke8HN
+ X5vIw9ssx8=LL00fxAuX9SRdcrtVyTYGXORXe9NnldXjBXmLPgwqJAjoBTjTBQxzrQOtdla=/yw
+ MsDlFWumPz1HAFw7R5zS2VCHrwkLDm=h7k3y+fUvYOx6IYf+MmevANuJT+2qY6s/ilTBNDYq6jJ
+ 8LYpsBo4XpQm1ZleFCIyRldHfmaC5EMxkVQVqCV7X9I6JgzDEetUre25LQTpDa31M=ucVHNWlT+
+ 6rjiLETNeMTWGcuIkLPe/PElmp4llKeFi6g2=E2AKeSDzNycr5eXHEnBuKfEnENXXo6n-----END RSA PRIVATE KEY-----
+:id: test-key
diff --git a/server/tests/drivers/mock/api_test.rb b/server/tests/drivers/mock/api_test.rb
index cfb0921..3308365 100644
--- a/server/tests/drivers/mock/api_test.rb
+++ b/server/tests/drivers/mock/api_test.rb
@@ -1,133 +1,115 @@
-# 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.
-#
-
-$:.unshift File.join(File.dirname(__FILE__), '..', '..', '..')
-require 'tests/common'
-
-module DeltacloudUnitTest
- class ApiTest < Test::Unit::TestCase
- include Rack::Test::Methods
-
- def app
- Sinatra::Application
- end
+describe 'Deltacloud API' do
+ include Deltacloud::Test
- def test_it_returns_entry_points
- get_auth_url '/api'
- (last_xml_response/'/api/link').length.should > 0
- end
+ it 'return HTTP_OK when accessing API entrypoint' do
+ get API_ROOT_URL
+ last_response.status.must_equal 200
+ end
- def test_it_has_correct_attributes_set
- get_auth_url '/api'
- (last_xml_response/'/api/link').each do |link|
- link.attributes.keys.sort.should == [ 'href', 'rel' ]
- end
- end
+ it 'advertise the current driver in API entrypoint' do
+ get API_ROOT_URL
+ xml_response.root[:driver].must_equal ENV['API_DRIVER']
+ end
- def test_it_responses_to_html
- get_url '/api', {}, { :format => :html }
- last_response.status.should == 200
- Nokogiri::HTML(last_response.body).search('html').first.name.should == 'html'
- end
+ it 'advertise the current API version in API entrypoint' do
+ get API_ROOT_URL
+ xml_response.root[:version].must_equal API_VERSION
+ end
- def test_it_responses_to_json
- get_url '/api', {}, { :format => :json }
- last_response.status.should == 200
- JSON::parse(last_response.body).class.should == Hash
- JSON::parse(last_response.body)['api'].class.should == Hash
- end
+ it 'advertise the current API version in HTTP headers' do
+ get API_ROOT_URL
+ last_response.headers['Server'].must_equal "Apache-Deltacloud/#{API_VERSION}"
+ end
- def test_it_switches_drivers
- with_provider("") do
- get_auth_url '/api'
- (last_xml_response/"api/link[rel = 'instances']").first.should_not == nil
- end
-
- # Switch to storage-only mock driver
- with_provider("storage") do
- get_auth_url '/api'
- (last_xml_response/"api/link[rel = 'instances']").first.should == nil
- end
- end
+ it 'must include the ETag in HTTP headers' do
+ get API_ROOT_URL
+ last_response.headers['ETag'].wont_be_nil
+ end
- def test_it_handles_unsupported_collections
- get_auth_url '/api/no_such_collection'
- last_response.status.should == 404
+ it 'advertise collections in API entrypoint' do
+ get API_ROOT_URL
+ (xml_response/'api/link').wont_be_empty
+ end
- with_provider("storage") do
- get_auth_url '/api/instances'
- last_response.status.should == 403
- end
+ it 'include the :href and :rel attribute for each collection in API entrypoint' do
+ get API_ROOT_URL
+ (xml_response/'api/link').each do |collection|
+ collection[:href].wont_be_nil
+ collection[:rel].wont_be_nil
end
+ end
- def test_it_allows_accessing_docs
- get_url '/api/docs/instances'
- last_response.status.should == 200
-
- with_provider("storage") do
- get_url '/api/docs/instances'
- last_response.status.should == 403
- end
+ it 'uses the absolute URI in the :href attribute for each collection in API entrypoint' do
+ get API_ROOT_URL
+ (xml_response/'api/link').each do |collection|
+ collection[:href].must_match /^http/
end
+ end
- def test_it_respond_to_head
- head '/api/instances'
- last_response.headers['Allow'].should_not == nil
- last_response.headers['Allow'].split(',').include?('HEAD').should == true
- end
+ it 'advertise features for some collections in API entrypoint' do
+ get API_ROOT_URL
+ (xml_response/'api/link/feature').wont_be_empty
+ end
- def test_it_expose_available_drivers
- get_auth_url '/api/drivers'
- last_response.status.should == 200
- (last_xml_response/"drivers").length.should > 0
- (last_xml_response/'drivers/driver').length.should > 0
- (last_xml_response/"drivers/driver[@id = 'mock']").length.should == 1
+ it 'advertise the name of the feature for some collections in API entrypoint' do
+ get API_ROOT_URL
+ (xml_response/'api/link/feature').each do |f|
+ f[:name].wont_be_nil
end
+ end
- def test_it_expose_ec2_driver_entrypoints
- get_auth_url '/api/drivers'
- last_response.status.should == 200
- ec2 = (last_xml_response/'drivers/driver[@id=ec2]').first
- (ec2/"provider").length.should > 0
- (ec2/"provider[@id = 'eu-west-1']").length.should == 1
- get_auth_url ec2[:href]
- eu_west = (last_xml_response/"provider[@id = 'eu-west-1']").first
- (eu_west/"entrypoint").length.should > 0
- (eu_west/"entrypoint[@kind = 'ec2']").length.should == 1
- end
+ it 'must change the media type from XML to JSON using Accept headers' do
+ header 'Accept', 'application/json'
+ get API_ROOT_URL
+ last_response.headers['Content-Type'].must_equal 'application/json'
+ end
- def test_it_supports_matrix_params
- get_auth_url "/api;driver=ec2"
- last_response.status.should == 200
- (last_xml_response/'api').first[:driver].should == 'ec2'
- get_auth_url "/api;driver=mock"
- (last_xml_response/'api').first[:driver].should == 'mock'
- get_auth_url "/api;driver=ec2/hardware_profiles"
- (last_xml_response/'hardware_profiles/hardware_profile/@id').map {|n| n.to_s}.include?('m1.small').should == true
- last_response.status.should == 200
- end
+ it 'must change the media type to JSON using the "?format" parameter in URL' do
+ get API_ROOT_URL, { :format => 'json' }
+ last_response.headers['Content-Type'].must_equal 'application/json'
+ end
- def test_it_change_features_after_driver_change
- get_auth_url "/api;driver=ec2"
- (last_xml_response/'api/link[@rel="instances"]/feature[@name="user_name"]').first.should == nil
- (last_xml_response/'api/link[@rel="instances"]/feature[@name="user_data"]').first.should_not == nil
- get_auth_url "/api;driver=mock"
- (last_xml_response/'api/link[@rel="instances"]/feature[@name="user_name"]').first.should_not == nil
- (last_xml_response/'api/link[@rel="instances"]/feature[@name="firewalls"]').first.should == nil
- end
+ it 'must change the driver when using X-Deltacloud-Driver HTTP header' do
+ header 'X-Deltacloud-Driver', 'ec2'
+ get API_ROOT_URL
+ xml_response.root[:driver].must_equal 'ec2'
+ header 'X-Deltacloud-Driver', 'mock'
+ get API_ROOT_URL
+ xml_response.root[:driver].must_equal 'mock'
+ end
+ it 'must change the features when driver is swapped using HTTP headers' do
+ header 'X-Deltacloud-Driver', 'ec2'
+ get API_ROOT_URL
+ # The 'user_name' feature is not supported currently for the EC2 driver
+ (xml_response/'api/link/feature').map { |f| f[:name] }.wont_include 'user_name'
+ header 'X-Deltacloud-Driver', 'mock'
+ get API_ROOT_URL
+ # But it's supported in Mock driver
+ (xml_response/'api/link/feature').map { |f| f[:name] }.must_include 'user_name'
end
+
+ it 'must re-validate the driver credentials when using "?force_auth" parameter in URL' do
+ get API_ROOT_URL, { :force_auth => '1' }
+ last_response.status.must_equal 401
+ auth_as_mock
+ get API_ROOT_URL, { :force_auth => '1' }
+ last_response.status.must_equal 200
+ end
+
+ it 'must change the API PROVIDER using the /api;provider matrix parameter in URI' do
+ get API_ROOT_URL + ';provider=test1'
+ xml_response.root[:provider].wont_be_nil
+ xml_response.root[:provider].must_equal 'test1'
+ get API_ROOT_URL + ';provider=test2'
+ xml_response.root[:provider].must_equal 'test2'
+ end
+
+ it 'must change the API DRIVER using the /api;driver matrix parameter in URI' do
+ get API_ROOT_URL + ';driver=ec2'
+ xml_response.root[:driver].must_equal 'ec2'
+ get API_ROOT_URL + ';driver=mock'
+ xml_response.root[:driver].must_equal 'mock'
+ end
+
end
diff --git a/server/tests/drivers/mock/buckets_test.rb b/server/tests/drivers/mock/buckets_test.rb
new file mode 100644
index 0000000..f98ad24
--- /dev/null
+++ b/server/tests/drivers/mock/buckets_test.rb
@@ -0,0 +1,192 @@
+describe 'Deltacloud API buckets' do
+ include Deltacloud::Test
+
+ it 'must advertise have the buckets collection in API entrypoint' do
+ get API_ROOT_URL
+ (xml_response/'api/link[@rel=buckets]').wont_be_empty
+ end
+
+ it 'must require authentication to access the "bucket" collection' do
+ get collection_url(:buckets)
+ last_response.status.must_equal 401
+ end
+
+ it 'should respond with HTTP_OK when accessing the :buckets collection with authentication' do
+ auth_as_mock
+ get collection_url(:buckets)
+ last_response.status.must_equal 200
+ end
+
+ it 'should support the JSON media type' do
+ auth_as_mock
+ header 'Accept', 'application/json'
+ get collection_url(:buckets)
+ last_response.status.must_equal 200
+ last_response.headers['Content-Type'].must_equal 'application/json'
+ end
+
+ it 'must include the ETag in HTTP headers' do
+ auth_as_mock
+ get collection_url(:buckets)
+ last_response.headers['ETag'].wont_be_nil
+ end
+
+ it 'must have the "buckets" element on top level' do
+ auth_as_mock
+ get collection_url(:buckets)
+ xml_response.root.name.must_equal 'buckets'
+ end
+
+ it 'must have some "bucket" elements inside "buckets"' do
+ auth_as_mock
+ get collection_url(:buckets)
+ (xml_response/'buckets/bucket').wont_be_empty
+ end
+
+ it 'must provide the :id attribute for each bucket in collection' do
+ auth_as_mock
+ get collection_url(:buckets)
+ (xml_response/'buckets/bucket').each do |r|
+ r[:id].wont_be_nil
+ end
+ end
+
+ it 'must include the :href attribute for each "bucket" element in collection' do
+ auth_as_mock
+ get collection_url(:buckets)
+ (xml_response/'buckets/bucket').each do |r|
+ r[:href].wont_be_nil
+ end
+ end
+
+ it 'must use the absolute URL in each :href attribute' do
+ auth_as_mock
+ get collection_url(:buckets)
+ (xml_response/'buckets/bucket').each do |r|
+ r[:href].must_match /^http/
+ end
+ end
+
+ it 'must have the URL ending with the :id of the bucket' do
+ auth_as_mock
+ get collection_url(:buckets)
+ (xml_response/'buckets/bucket').each do |r|
+ r[:href].must_match /#{r[:id]}$/
+ end
+ end
+
+ it 'must return the list of valid parameters for the :index action' do
+ auth_as_mock
+ options collection_url(:buckets) + '/index'
+ last_response.headers['Allow'].wont_be_nil
+ end
+
+ it 'must have the "name" element defined for each bucket in collection' do
+ auth_as_mock
+ get collection_url(:buckets)
+ (xml_response/'buckets/bucket').each do |r|
+ (r/'name').wont_be_nil
+ end
+ end
+
+ it 'must have the "state" element defined for each bucket in collection' do
+ auth_as_mock
+ get collection_url(:buckets)
+ (xml_response/'buckets/bucket').each do |r|
+ (r/'state').wont_be_nil
+ end
+ end
+
+ it 'must return the full "bucket" when following the URL in bucket element' do
+ auth_as_mock
+ get collection_url(:buckets)
+ (xml_response/'buckets/bucket').each do |r|
+ get collection_url(:buckets) + '/' + r[:id]
+ last_response.status.must_equal 200
+ end
+ end
+
+ it 'must have the "name" element for the bucket and it should match with the one in collection' do
+ auth_as_mock
+ get collection_url(:buckets)
+ (xml_response/'buckets/bucket').each do |r|
+ get collection_url(:buckets) + '/' + r[:id]
+ (xml_response/'name').wont_be_empty
+ (xml_response/'name').first.text.must_equal((r/'name').first.text)
+ end
+ end
+
+ it 'must have the "size" element for the bucket and it should match with the one in collection' do
+ auth_as_mock
+ get collection_url(:buckets)
+ (xml_response/'buckets/bucket').each do |r|
+ get collection_url(:buckets) + '/' + r[:id]
+ (xml_response/'size').wont_be_empty
+ (xml_response/'size').first.text.must_equal((r/'size').first.text)
+ end
+ end
+
+ it 'must have the "blob" elements for the bucket and it should match with the ones in collection' do
+ auth_as_mock
+ get collection_url(:buckets)
+ (xml_response/'buckets/bucket').each do |r|
+ get collection_url(:buckets) + '/' + r[:id]
+ (xml_response/'bucket/blob').wont_be_empty
+ (xml_response/'bucket/blob').each do |b|
+ b[:id].wont_be_nil
+ b[:href].wont_be_nil
+ b[:href].must_match /^http/
+ b[:href].must_match /#{r[:id]}\/#{b[:id]}$/
+ end
+ end
+ end
+
+ it 'must have the "blob" elements for the bucket and it should match with the ones in collection' do
+ auth_as_mock
+ get collection_url(:buckets)
+ (xml_response/'buckets/bucket').each do |r|
+ get collection_url(:buckets) + '/' + r[:id]
+ (xml_response/'bucket/blob').wont_be_empty
+ (xml_response/'bucket/blob').each do |b|
+ b[:id].wont_be_nil
+ b[:href].wont_be_nil
+ b[:href].must_match /^http/
+ b[:href].must_match /#{r[:id]}\/#{b[:id]}$/
+ end
+ end
+ end
+
+ it 'must allow to get all blobs details and the details should be set correctly' do
+ auth_as_mock
+ get collection_url(:buckets)
+ (xml_response/'buckets/bucket').each do |r|
+ get collection_url(:buckets) + '/' + r[:id]
+ (xml_response/'bucket/blob').each do |b|
+ get collection_url(:buckets) + '/' + r[:id] + '/' + b[:id]
+ xml_response.root.name.must_equal 'blob'
+ xml_response.root[:id].must_equal b[:id]
+ (xml_response/'bucket').wont_be_empty
+ (xml_response/'bucket').size.must_equal 1
+ (xml_response/'bucket').first[:id].wont_be_nil
+ (xml_response/'bucket').first[:href].wont_be_nil
+ (xml_response/'content_length').wont_be_empty
+ (xml_response/'content_length').size.must_equal 1
+ (xml_response/'content_length').first.text.must_match /^(\d+)$/
+ (xml_response/'content_type').wont_be_empty
+ (xml_response/'content_type').size.must_equal 1
+ (xml_response/'content_type').first.text.wont_be_empty
+ (xml_response/'last_modified').wont_be_empty
+ (xml_response/'last_modified').size.must_equal 1
+ (xml_response/'last_modified').first.text.wont_be_empty
+ (xml_response/'content').wont_be_empty
+ (xml_response/'content').size.must_equal 1
+ (xml_response/'content').first[:rel].wont_be_nil
+ (xml_response/'content').first[:rel].must_equal 'blob_content'
+ (xml_response/'content').first[:href].wont_be_nil
+ (xml_response/'content').first[:href].must_match /^http/
+ (xml_response/'content').first[:href].must_match /\/content$/
+ end
+ end
+ end
+
+end
diff --git a/server/tests/drivers/mock/common.rb b/server/tests/drivers/mock/common.rb
new file mode 100644
index 0000000..08650a3
--- /dev/null
+++ b/server/tests/drivers/mock/common.rb
@@ -0,0 +1,58 @@
+unless Kernel.respond_to?(:require_relative)
+ module Kernel
+ def require_relative(path)
+ require File.join(File.dirname(caller[0]), path.to_str)
+ end
+ end
+end
+
+API_ROOT_URL = "/api" unless defined?(API_ROOT_URL)
+API_VERSION = "1.0.0" unless defined?(API_VERSION)
+ENV['API_DRIVER'] ||= 'mock'
+
+ENV['API_USERNAME'] ||= 'mockuser'
+ENV['API_PASSWORD'] ||= 'mockpassword'
+
+require_relative '../../../lib/deltacloud/server.rb'
+
+require 'minitest/autorun'
+require 'rack/test'
+require 'nokogiri'
+require 'json'
+
+require 'pp'
+
+module Deltacloud
+ module Test
+ include Rack::Test::Methods
+
+ def included?(sub)
+ sub.class_eval do
+ before do
+ header 'Accept', 'application/xml'
+ end
+ end
+ end
+
+ def xml_response
+ Nokogiri::XML(last_response.body)
+ end
+
+ def auth_as_mock
+ authorize ENV['API_USERNAME'], ENV['API_PASSWORD']
+ end
+
+ def collection_url(collection)
+ [API_ROOT_URL, collection.to_s].join('/')
+ end
+
+ def app
+ Rack::Builder.new {
+ map '/' do
+ use Rack::Static, :urls => ["/stylesheets", "/javascripts"], :root => "public"
+ run Rack::Cascade.new([Deltacloud::API])
+ end
+ }
+ end
+ end
+end
diff --git a/server/tests/drivers/mock/drivers_test.rb b/server/tests/drivers/mock/drivers_test.rb
new file mode 100644
index 0000000..41c2e66
--- /dev/null
+++ b/server/tests/drivers/mock/drivers_test.rb
@@ -0,0 +1,120 @@
+describe 'Deltacloud API drivers' do
+ include Deltacloud::Test
+
+ it 'must advertise have the drivers collection in API entrypoint' do
+ get API_ROOT_URL
+ (xml_response/'api/link[@rel=drivers]').wont_be_empty
+ end
+
+ it 'must not require authentication to access the "driver" collection' do
+ get collection_url(:drivers)
+ last_response.status.must_equal 200
+ end
+
+ it 'should respond with HTTP_OK when accessing the :drivers collection with authentication' do
+ get collection_url(:drivers)
+ last_response.status.must_equal 200
+ end
+
+ it 'should support the JSON media type' do
+ header 'Accept', 'application/json'
+ get collection_url(:drivers)
+ last_response.status.must_equal 200
+ last_response.headers['Content-Type'].must_equal 'application/json'
+ end
+
+ it 'must include the ETag in HTTP headers' do
+ get collection_url(:drivers)
+ last_response.headers['ETag'].wont_be_nil
+ end
+
+ it 'must have the "drivers" element on top level' do
+ get collection_url(:drivers)
+ xml_response.root.name.must_equal 'drivers'
+ end
+
+ it 'must have some "driver" elements inside "drivers"' do
+ get collection_url(:drivers)
+ (xml_response/'drivers/driver').wont_be_empty
+ end
+
+ it 'must provide the :id attribute for each driver in collection' do
+ get collection_url(:drivers)
+ (xml_response/'drivers/driver').each do |r|
+ r[:id].wont_be_nil
+ end
+ end
+
+ it 'must include the :href attribute for each "driver" element in collection' do
+ get collection_url(:drivers)
+ (xml_response/'drivers/driver').each do |r|
+ r[:href].wont_be_nil
+ end
+ end
+
+ it 'must use the absolute URL in each :href attribute' do
+ get collection_url(:drivers)
+ (xml_response/'drivers/driver').each do |r|
+ r[:href].must_match /^http/
+ end
+ end
+
+ it 'must have the URL ending with the :id of the driver' do
+ get collection_url(:drivers)
+ (xml_response/'drivers/driver').each do |r|
+ r[:href].must_match /#{r[:id]}$/
+ end
+ end
+
+ it 'must return the list of valid parameters for the :index action' do
+ options collection_url(:drivers) + '/index'
+ last_response.headers['Allow'].wont_be_nil
+ end
+
+ it 'must have the "name" element defined for each driver in collection' do
+ get collection_url(:drivers)
+ (xml_response/'drivers/driver').each do |r|
+ (r/'name').wont_be_nil
+ end
+ end
+
+
+ it 'must return the full "driver" when following the URL in driver element' do
+ get collection_url(:drivers)
+ (xml_response/'drivers/driver').each do |r|
+ get collection_url(:drivers) + '/' + r[:id]
+ last_response.status.must_equal 200
+ end
+ end
+
+ it 'must have the "name" element for the driver and it should match with the one in collection' do
+ get collection_url(:drivers)
+ (xml_response/'drivers/driver').each do |r|
+ get collection_url(:drivers) + '/' + r[:id]
+ (xml_response/'name').wont_be_empty
+ (xml_response/'name').first.text.must_equal((r/'name').first.text)
+ end
+ end
+
+ it 'should advertise available providers for some drivers' do
+ get collection_url(:drivers)
+ (xml_response/'drivers/driver/provider').each do |p|
+ p[:id].wont_be_nil
+ end
+ end
+
+ it 'should expose entrypoints for each provider if driver has providers defined' do
+ get collection_url(:drivers)
+ (xml_response/'drivers/driver/provider').each do |p|
+ get collection_url(:drivers) + '/' + p.parent[:id]
+ (xml_response/"driver/provider[@id=#{p[:id]}]").wont_be_empty
+ (xml_response/"driver/provider[@id=#{p[:id]}]").size.must_equal 1
+ (xml_response/"driver/provider[@id=#{p[:id]}]/entrypoint").wont_be_empty
+ (xml_response/"driver/provider[@id=#{p[:id]}]/entrypoint").each do |e|
+ e[:kind].wont_be_nil
+ e.text.wont_be_empty
+ end
+ end
+ end
+
+end
diff --git a/server/tests/drivers/mock/hardware_profiles_test.rb b/server/tests/drivers/mock/hardware_profiles_test.rb
index 47f7eb3..3dad5a8 100644
--- a/server/tests/drivers/mock/hardware_profiles_test.rb
+++ b/server/tests/drivers/mock/hardware_profiles_test.rb
@@ -1,134 +1,221 @@
-# 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.
-#
-
-$:.unshift File.join(File.dirname(__FILE__), '..', '..', '..')
-require 'tests/common'
-
-module DeltacloudUnitTest
- class HardwareProfilesTest < Test::Unit::TestCase
- include Rack::Test::Methods
-
- def app
- Sinatra::Application
- end
-
- def test_it_returns_hardware_profiles
- get_url '/api/hardware_profiles'
- (last_xml_response/'hardware_profiles/hardware_profile').length.should > 0
- end
-
- def test_it_has_correct_attributes_set
- get_auth_url '/api/hardware_profiles'
- (last_xml_response/'hardware_profiles/hardware_profile').each do |profile|
- profile.attributes.keys.sort.should == [ 'href', 'id' ]
- end
+describe 'Deltacloud API Hardware Profiles' do
+ include Deltacloud::Test
+
+ it 'must advertise have the hardware_profiles collection in API entrypoint' do
+ get API_ROOT_URL
+ (xml_response/'api/link[@rel=hardware_profiles]').wont_be_empty
+ end
+
+ it 'should respond with HTTP_OK when accessing the :hardware_profiles collection with authentication' do
+ auth_as_mock
+ get collection_url(:hardware_profiles)
+ last_response.status.must_equal 200
+ end
+
+ it 'should support the JSON media type' do
+ auth_as_mock
+ header 'Accept', 'application/json'
+ get collection_url(:hardware_profiles)
+ last_response.status.must_equal 200
+ last_response.headers['Content-Type'].must_equal 'application/json'
+ end
+
+ it 'must include the ETag in HTTP headers' do
+ auth_as_mock
+ get collection_url(:hardware_profiles)
+ last_response.headers['ETag'].wont_be_nil
+ end
+
+ it 'must have the "hardware_profiles" element on top level' do
+ auth_as_mock
+ get collection_url(:hardware_profiles)
+ xml_response.root.name.must_equal 'hardware_profiles'
+ end
+
+ it 'must have some "hardware_profile" elements inside "hardware_profiles"' do
+ auth_as_mock
+ get collection_url(:hardware_profiles)
+ (xml_response/'hardware_profiles/hardware_profile').wont_be_empty
+ end
+
+ it 'must provide the :id attribute for each hardware_profile in collection' do
+ auth_as_mock
+ get collection_url(:hardware_profiles)
+ (xml_response/'hardware_profiles/hardware_profile').each do |r|
+ r[:id].wont_be_nil
end
+ end
- def test_hardware_profiles_have_name
- get_auth_url '/api/hardware_profiles'
- (last_xml_response/'hardware_profiles/hardware_profile').each do |profile|
- (profile/'name').text.should_not == nil
- end
+ it 'must include the :href attribute for each "hardware_profile" element in collection' do
+ auth_as_mock
+ get collection_url(:hardware_profiles)
+ (xml_response/'hardware_profiles/hardware_profile').each do |r|
+ r[:href].wont_be_nil
end
+ end
- def test_hardware_profiles_have_unique_name
- get_auth_url '/api/hardware_profiles'
- names = []
- (last_xml_response/'hardware_profiles/hardware_profile').each do |profile|
- names << (profile/'name').text
- end
- names.should == names.uniq
+ it 'must use the absolute URL in each :href attribute' do
+ auth_as_mock
+ get collection_url(:hardware_profiles)
+ (xml_response/'hardware_profiles/hardware_profile').each do |r|
+ r[:href].must_match /^http/
end
+ end
- def test_hardware_profiles_have_unique_id
- get_auth_url '/api/hardware_profiles'
- ids = []
- (last_xml_response/'hardware_profiles/hardware_profile').each do |profile|
- ids << profile['id']
- end
- ids.should == ids.uniq
+ it 'must have the URL ending with the :id of the hardware_profile' do
+ auth_as_mock
+ get collection_url(:hardware_profiles)
+ (xml_response/'hardware_profiles/hardware_profile').each do |r|
+ r[:href].must_match /#{r[:id]}$/
end
+ end
- def test_m1_xlarge_profile_has_correct_attributes
- get_auth_url '/api/hardware_profiles'
- profile = (last_xml_response/'hardware_profiles/hardware_profile[@id="m1-xlarge"]')
- test_profile_properties(profile)
+ it 'must return the list of valid parameters for the :index action' do
+ auth_as_mock
+ options collection_url(:hardware_profiles) + '/index'
+ last_response.headers['Allow'].wont_be_nil
+ end
+
+ it 'must have the "name" element defined for each hardware_profile in collection' do
+ auth_as_mock
+ get collection_url(:hardware_profiles)
+ (xml_response/'hardware_profiles/hardware_profile').each do |r|
+ (r/'name').wont_be_empty
end
+ end
- def test_it_returns_valid_hardware_profile
- get_auth_url '/api/hardware_profiles/m1-xlarge'
- profile = (last_xml_response/'hardware_profile')
- test_profile_properties(profile)
+ it 'should have the "property" element defined if not the opaque hardware_profile' do
+ auth_as_mock
+ get collection_url(:hardware_profiles)
+ (xml_response/'hardware_profiles/hardware_profile').each do |r|
+ next if r[:id] == 'opaque'
+ (r/'property').wont_be_empty
end
+ end
- def test_it_responses_to_json
- get_url '/api/hardware_profiles', {}, { :format => :json }
- JSON::parse(last_response.body).class.should == Hash
- JSON::parse(last_response.body)['hardware_profiles'].class.should == Array
- get_url '/api/hardware_profiles/m1-xlarge', {}, { :format => :json }
- last_response.status.should == 200
- JSON::parse(last_response.body).class.should == Hash
- JSON::parse(last_response.body)['hardware_profile'].class.should == Hash
+ it 'must define the :kind attribute for each "property" ' do
+ auth_as_mock
+ get collection_url(:hardware_profiles)
+ (xml_response/'hardware_profiles/hardware_profile').each do |r|
+ next if r[:id] == 'opaque'
+ (r/'property').each { |p| p[:kind].wont_be_nil }
end
+ end
- def test_it_responses_to_html
- get_url '/api/hardware_profiles', {}, { :format => :html }
- last_response.status.should == 200
- Nokogiri::HTML(last_response.body).search('html').first.name.should == 'html'
- get_url '/api/hardware_profiles/m1-xlarge', {}, { :format => :html }
- last_response.status.should == 200
- Nokogiri::HTML(last_response.body).search('html').first.name.should == 'html'
+ it 'must define the :name attribute for each "property" ' do
+ auth_as_mock
+ get collection_url(:hardware_profiles)
+ (xml_response/'hardware_profiles/hardware_profile').each do |r|
+ next if r[:id] == 'opaque'
+ (r/'property').each { |p| p[:name].wont_be_nil }
end
+ end
- def test_it_returns_error_on_wrong_name
- get_url '/api/hardware_profiles/m1-unknown-wrongname', {}, { :format => :html }
- last_response.status.should == 404
- get_auth_url '/api/hardware_profiles/m1-unknown-wrongname'
- last_response.status.should == 404
- get_url '/api/hardware_profiles/m1-unknown-wrongname', {}, { :format => :json }
- last_response.status.should == 404
+ it 'must define the :unit attribute for each "property" ' do
+ auth_as_mock
+ get collection_url(:hardware_profiles)
+ (xml_response/'hardware_profiles/hardware_profile').each do |r|
+ next if r[:id] == 'opaque'
+ (r/'property').each { |p| p[:unit].wont_be_nil }
end
+ end
- private
+ it 'must define the :value attribute for each "property" ' do
+ auth_as_mock
+ get collection_url(:hardware_profiles)
+ (xml_response/'hardware_profiles/hardware_profile').each do |r|
+ next if r[:id] == 'opaque'
+ (r/'property').each { |p| p[:value].wont_be_nil }
+ end
+ end
- def test_profile_properties(profile)
- (profile/'property').each do |properties|
- properties.attributes.keys.sort.should == [ 'kind', 'name', 'unit', 'value' ]
+ it 'must define the "param" element if property kind is not "fixed"' do
+ auth_as_mock
+ get collection_url(:hardware_profiles)
+ (xml_response/'hardware_profiles/hardware_profile').each do |r|
+ next if r[:id] == 'opaque'
+ (r/'property').each do |p|
+ next if p[:kind] == 'fixed'
+ (p/'param').wont_be_empty
+ (p/'param').size.must_equal 1
+ (p/'param').first[:href].wont_be_nil
+ (p/'param').first[:href].must_match /^http/
+ (p/'param').first[:method].wont_be_nil
+ (p/'param').first[:name].wont_be_nil
+ (p/'param').first[:operation].wont_be_nil
end
+ end
+ end
- (profile/'property[@name="architecture"]').first['kind'].should == 'fixed'
- (profile/'property[@name="architecture"]').first['unit'].should == 'label'
+ it 'must provide the list of valid values when the property is defined as "enum"' do
+ auth_as_mock
+ get collection_url(:hardware_profiles)
+ (xml_response/'hardware_profiles/hardware_profile').each do |r|
+ next if r[:id] == 'opaque'
+ (r/'property').each do |p|
+ next if p[:kind] != 'enum'
+ (p/'enum/entry').wont_be_empty
+ (p/'enum/entry').each { |e| e[:value].wont_be_nil }
+ end
+ end
+ end
- (profile/'property[@name="memory"]').first['kind'].should == 'range'
- (profile/'property[@name="memory"]').first['unit'].should == 'MB'
- (profile/'property[@name="memory"]/range').length.should == 1
- (profile/'property[@name="memory"]/range').first.attributes.keys.sort.should == [ 'first', 'last' ]
+ it 'must provide the range of valid values when the property is defined as "range"' do
+ auth_as_mock
+ get collection_url(:hardware_profiles)
+ (xml_response/'hardware_profiles/hardware_profile').each do |r|
+ next if r[:id] == 'opaque'
+ (r/'property').each do |p|
+ next if p[:kind] != 'range'
+ (p/'range').wont_be_empty
+ (p/'range').size.must_equal 1
+ (p/'range').first[:first].wont_be_nil
+ (p/'range').first[:last].wont_be_nil
+ end
+ end
+ end
- (profile/'property[@name="cpu"]').first['kind'].should == 'fixed'
- (profile/'property[@name="cpu"]').first['unit'].should == 'count'
+ it 'must provide the default value within the range if property defined as "range"' do
+ auth_as_mock
+ get collection_url(:hardware_profiles)
+ (xml_response/'hardware_profiles/hardware_profile').each do |r|
+ next if r[:id] == 'opaque'
+ (r/'property').each do |p|
+ next if p[:kind] != 'range'
+ ((p/'range').first[:first].to_i..(p/'range').first[:last].to_i).include?(p[:value].to_i).must_equal true
+ end
+ end
+ end
- (profile/'property[@name="storage"]').first['kind'].should == 'enum'
- (profile/'property[@name="storage"]').first['unit'].should == 'GB'
- (profile/'property[@name="storage"]/enum').length.should == 1
- (profile/'property[@name="storage"]/enum/entry').length.should == 3
- (profile/'property[@name="storage"]/enum/entry').each do |entry|
- entry.attributes.keys.should == [ 'value' ]
- entry['value'].should_not == nil
+ it 'must provide the default value that is included in enum list if property defined as "enum"' do
+ auth_as_mock
+ get collection_url(:hardware_profiles)
+ (xml_response/'hardware_profiles/hardware_profile').each do |r|
+ next if r[:id] == 'opaque'
+ (r/'property').each do |p|
+ next if p[:kind] != 'enum'
+ (p/'enum/entry').map { |e| e[:value] }.include?(p[:value]).must_equal true
end
end
+ end
+ it 'must return the full "hardware_profile" when following the URL in hardware_profile element' do
+ auth_as_mock
+ get collection_url(:hardware_profiles)
+ (xml_response/'hardware_profiles/hardware_profile').each do |r|
+ get collection_url(:hardware_profiles) + '/' + r[:id]
+ last_response.status.must_equal 200
+ end
end
+
+ it 'must have the "name" element for the hardware_profile and it should match with the one in collection' do
+ auth_as_mock
+ get collection_url(:hardware_profiles)
+ (xml_response/'hardware_profiles/hardware_profile').each do |r|
+ get collection_url(:hardware_profiles) + '/' + r[:id]
+ (xml_response/'name').wont_be_empty
+ (xml_response/'name').first.text.must_equal((r/'name').first.text)
+ end
+ end
+
end
diff --git a/server/tests/drivers/mock/images_test.rb b/server/tests/drivers/mock/images_test.rb
index 47fb690..3faf752 100644
--- a/server/tests/drivers/mock/images_test.rb
+++ b/server/tests/drivers/mock/images_test.rb
@@ -1,138 +1,194 @@
-# 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.
-#
-
-$:.unshift File.join(File.dirname(__FILE__), '..', '..', '..')
-require 'tests/common'
-
-module DeltacloudUnitTest
- class HardwareProfilesTest < Test::Unit::TestCase
- include Rack::Test::Methods
-
- def app
- Sinatra::Application
- end
+describe 'Deltacloud API Images' do
+ include Deltacloud::Test
- def test_it_require_authentication
- require_authentication?('/api/images').should == true
- end
+ it 'must advertise have the images collection in API entrypoint' do
+ get API_ROOT_URL
+ (xml_response/'api/link[@rel=images]').wont_be_empty
+ end
- def test_it_returns_images
- get_auth_url '/api/images', {}
- (last_xml_response/'images/image').length.should > 0
- end
+ it 'must require authentication to access the "image" collection' do
+ get collection_url(:images)
+ last_response.status.must_equal 401
+ end
- def test_it_has_correct_attributes_set
- get_auth_url '/api/images', {}
- (last_xml_response/'images/image').each do |image|
- image.attributes.keys.sort.should == [ 'href', 'id' ]
- end
+ it 'should respond with HTTP_OK when accessing the :images collection with authentication' do
+ auth_as_mock
+ get collection_url(:images)
+ last_response.status.must_equal 200
+ end
+
+ it 'should support the JSON media type' do
+ auth_as_mock
+ header 'Accept', 'application/json'
+ get collection_url(:images)
+ last_response.status.must_equal 200
+ last_response.headers['Content-Type'].must_equal 'application/json'
+ end
+
+ it 'must include the ETag in HTTP headers' do
+ auth_as_mock
+ get collection_url(:images)
+ last_response.headers['ETag'].wont_be_nil
+ end
+
+ it 'must have the "images" element on top level' do
+ auth_as_mock
+ get collection_url(:images)
+ xml_response.root.name.must_equal 'images'
+ end
+
+ it 'must have some "image" elements inside "images"' do
+ auth_as_mock
+ get collection_url(:images)
+ (xml_response/'images/image').wont_be_empty
+ end
+
+ it 'must provide the :id attribute for each image in collection' do
+ auth_as_mock
+ get collection_url(:images)
+ (xml_response/'images/image').each do |r|
+ r[:id].wont_be_nil
end
+ end
- def test_img1_has_correct_attributes
- get_auth_url '/api/images', {}
- image = (last_xml_response/'images/image[@id="img1"]')
- test_image_attributes(image)
+ it 'must include the :href attribute for each "image" element in collection' do
+ auth_as_mock
+ get collection_url(:images)
+ (xml_response/'images/image').each do |r|
+ r[:href].wont_be_nil
end
+ end
- def test_it_returns_valid_image
- get_auth_url '/api/images/img1', {}
- image = (last_xml_response/'image')
- test_image_attributes(image)
+ it 'must use the absolute URL in each :href attribute' do
+ auth_as_mock
+ get collection_url(:images)
+ (xml_response/'images/image').each do |r|
+ r[:href].must_match /^http/
end
+ end
- def test_it_has_unique_ids
- get_auth_url '/api/images', {}
- ids = []
- (last_xml_response/'images/image').each do |image|
- ids << image['id'].to_s
- end
- ids.sort.should == ids.sort.uniq
+ it 'must have the URL ending with the :id of the image' do
+ auth_as_mock
+ get collection_url(:images)
+ (xml_response/'images/image').each do |r|
+ r[:href].must_match /#{r[:id]}$/
end
+ end
- def test_it_has_valid_urls
- get_auth_url '/api/images', {}
- ids = []
- images = (last_xml_response/'images/image')
- images.each do |image|
- get_auth_url image['href'].to_s, {}
- (last_xml_response/'image').first['href'].should == image['href'].to_s
- end
+ it 'must return the list of valid parameters for the :index action' do
+ auth_as_mock
+ options collection_url(:images) + '/index'
+ last_response.headers['Allow'].wont_be_nil
+ end
+
+ it 'must have the "name" element defined for each image in collection' do
+ auth_as_mock
+ get collection_url(:images)
+ (xml_response/'images/image').each do |r|
+ (r/'name').wont_be_empty
end
+ end
- def test_it_can_filter_using_owner_id
- get_auth_url '/api/images', { :owner_id => 'mockuser' }
- (last_xml_response/'images/image').length.should == 1
- (last_xml_response/'images/image/owner_id').first.text.should == 'mockuser'
+ it 'must have the "state" element defined for each image in collection' do
+ auth_as_mock
+ get collection_url(:images)
+ (xml_response/'images/image').each do |r|
+ (r/'state').wont_be_empty
end
+ end
- def test_it_can_filter_using_unknown_owner_id
- get_auth_url '/api/images', { :architecture => 'unknown_user' }
- (last_xml_response/'images/image').length.should == 0
+ it 'must return the full "image" when following the URL in image element' do
+ auth_as_mock
+ get collection_url(:images)
+ (xml_response/'images/image').each do |r|
+ get collection_url(:images) + '/' + r[:id]
+ last_response.status.must_equal 200
end
+ end
- def test_it_can_filter_using_architecture
- get_auth_url '/api/images', { :architecture => 'x86_64' }
- (last_xml_response/'images/image').length.should == 1
- (last_xml_response/'images/image/architecture').first.text.should == 'x86_64'
+ it 'must have the "name" element for the image and it should match with the one in collection' do
+ auth_as_mock
+ get collection_url(:images)
+ (xml_response/'images/image').each do |r|
+ get collection_url(:images) + '/' + r[:id]
+ (xml_response/'name').wont_be_empty
+ (xml_response/'name').first.text.must_equal((r/'name').first.text)
end
+ end
- def test_it_can_filter_using_unknown_architecture
- get_auth_url '/api/images', { :architecture => 'unknown_arch' }
- (last_xml_response/'images/image').length.should == 0
+ it 'must have the "name" element for the image and it should match with the one in collection' do
+ auth_as_mock
+ get collection_url(:images)
+ (xml_response/'images/image').each do |r|
+ get collection_url(:images) + '/' + r[:id]
+ (xml_response/'state').wont_be_empty
+ (xml_response/'state').first.text.must_equal((r/'state').first.text)
end
+ end
- def test_it_responses_to_json
- get_auth_url '/api/images', {}, { :format => :json }
- JSON::parse(last_response.body).class.should == Hash
- JSON::parse(last_response.body)['images'].class.should == Array
- get_auth_url '/api/images/img1', {}, { :format => :json }
- last_response.status.should == 200
- JSON::parse(last_response.body).class.should == Hash
- JSON::parse(last_response.body)['image'].class.should == Hash
+ it 'should have the "owner_id" element for each image' do
+ auth_as_mock
+ get collection_url(:images)
+ (xml_response/'images/image').each do |r|
+ get collection_url(:images) + '/' + r[:id]
+ (xml_response/'owner_id').wont_be_empty
end
+ end
- def test_it_responses_to_html
- get_auth_url '/api/images', {}, { :format => :html }
- last_response.status.should == 200
- Nokogiri::HTML(last_response.body).search('html').first.name.should == 'html'
- get_auth_url '/api/images/img1', {}, { :format => :html }
- last_response.status.should == 200
- Nokogiri::HTML(last_response.body).search('html').first.name.should == 'html'
+ it 'should have the "description" element for each image' do
+ auth_as_mock
+ get collection_url(:images)
+ (xml_response/'images/image').each do |r|
+ get collection_url(:images) + '/' + r[:id]
+ (xml_response/'description').wont_be_empty
end
+ end
- def test_it_creates_and_destroys_image_from_instance
- post_url "/api/images", { :name => "img4", :description => "Test::Unit image", :instance_id => "inst1"}
- last_response.status.should == 201
- last_response.headers['Location'].should_not == nil
- get_auth_url last_response.headers['Location'], {}
- (last_xml_response/'instance/name').should_not == nil
- delete_url "/api/images/img4", {}
- last_response.status.should == 204
- get_auth_url "/api/images/img4", {}
- last_response.status.should == 404
+ it 'should have the "architecture" element for each image' do
+ auth_as_mock
+ get collection_url(:images)
+ (xml_response/'images/image').each do |r|
+ get collection_url(:images) + '/' + r[:id]
+ (xml_response/'architecture').wont_be_empty
end
+ end
- private
+ it 'should include the list of compatible hardware_profiles for each image' do
+ auth_as_mock
+ get collection_url(:images)
+ (xml_response/'images/image').each do |r|
+ get collection_url(:images) + '/' + r[:id]
+ (xml_response/'hardware_profiles/hardware_profile').wont_be_empty
+ (xml_response/'hardware_profiles/hardware_profile').each do |hwp|
+ hwp[:href].wont_be_nil
+ hwp[:href].must_match /^http/
+ hwp[:id].wont_be_nil
+ hwp[:href].must_match /\/#{hwp[:id]}$/
+ hwp[:rel].must_equal 'hardware_profile'
+ end
+ end
+ end
- def test_image_attributes(image)
- (image/'name').text.should_not nil
- (image/'owner_id').text.should_not nil
- (image/'description').text.should_not nil
- (image/'architecture').text.should_not nil
+ it 'should advertise the list of actions that can be executed for each image' do
+ auth_as_mock
+ get collection_url(:images)
+ (xml_response/'images/image').each do |r|
+ get collection_url(:images) + '/' + r[:id]
+ (xml_response/'actions/link').wont_be_empty
+ (xml_response/'actions/link').each do |l|
+ l[:href].wont_be_nil
+ l[:href].must_match /^http/
+ l[:method].wont_be_nil
+ l[:rel].wont_be_nil
+ end
end
+ end
+ it 'should give client HTML form to create new image' do
+ auth_as_mock
+ header 'Accept', 'text/html'
+ get collection_url(:images) + '/new'
+ last_response.status.must_equal 200
end
+
end
diff --git a/server/tests/drivers/mock/instance_states_test.rb b/server/tests/drivers/mock/instance_states_test.rb
deleted file mode 100644
index 905cff3..0000000
--- a/server/tests/drivers/mock/instance_states_test.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-# 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.
-#
-
-$:.unshift File.join(File.dirname(__FILE__), '..', '..', '..')
-require 'tests/common'
-
-module DeltacloudUnitTest
- class RealmsTest < Test::Unit::TestCase
- include Rack::Test::Methods
-
- def app
- Sinatra::Application
- end
-
- def test_it_returns_instance_states
- get_auth_url '/api/instance_states', {}
- (last_xml_response/'states/state').length.should > 0
- end
-
- def test_each_state_has_transition
- get_auth_url '/api/instance_states', {}
- (last_xml_response/'states/state').each do |state|
- next if state['name'].eql?('finish') # Finnish state doesn't have transitions
- (state/'transition').length.should > 0
- (state/'transition').each do |transition|
- transition['to'].should_not == nil
- end
- end
- end
-
- def test_it_responses_to_json
- # FIXME: This test is suffering from conflict between JSON gem and Activesupport
- # gem in EC2.
- #
- #do_request '/api/instance_states', {}, false, { :format => :json }
- #JSON::parse(last_response.body).class.should == Array
- #JSON::parse(last_response.body).first['transitions'].class.should == Array
- #JSON::parse(last_response.body).first['name'].should == 'start'
- end
-
- def test_it_responses_to_html
- get_url '/api/instance_states', {}, { :format => :html }
- last_response.status.should == 200
- Nokogiri::HTML(last_response.body).search('html').first.name.should == 'html'
- end
-
- def test_it_responses_to_png
- get_url '/api/instance_states', { :format => 'png' }
- last_response.status.should == 200
- last_response.headers['Content-Type'].should =~ /^image\/png/
- end
-
- end
-end
diff --git a/server/tests/drivers/mock/instances_test.rb b/server/tests/drivers/mock/instances_test.rb
index 45bb4b8..c601a6f 100644
--- a/server/tests/drivers/mock/instances_test.rb
+++ b/server/tests/drivers/mock/instances_test.rb
@@ -1,253 +1,340 @@
-# 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.
-#
-
-$:.unshift File.join(File.dirname(__FILE__), '..', '..', '..')
-require 'tests/common'
-
-module DeltacloudUnitTest
- class InstancesTest < Test::Unit::TestCase
- include Rack::Test::Methods
-
- def app
- Sinatra::Application
- end
-
- def test_it_require_authentication
- require_authentication?('/api/instances').should == true
- end
-
- def test_it_returns_instances
- get_auth_url '/api/instances', {}
- (last_xml_response/'instances/instance').length.should > 0
- end
-
- def test_it_has_correct_attributes_set
- get_auth_url '/api/images', {}
- (last_xml_response/'images/image').each do |image|
- image.attributes.keys.sort.should == [ 'href', 'id' ]
- end
+describe 'Deltacloud API instances' do
+ include Deltacloud::Test
+
+ it 'must advertise have the instances collection in API entrypoint' do
+ get API_ROOT_URL
+ (xml_response/'api/link[@rel=instances]').wont_be_empty
+ end
+
+ it 'must require authentication to access the "instance" collection' do
+ get collection_url(:instances)
+ last_response.status.must_equal 401
+ end
+
+ it 'should respond with HTTP_OK when accessing the :instances collection with authentication' do
+ auth_as_mock
+ get collection_url(:instances)
+ last_response.status.must_equal 200
+ end
+
+ it 'should support the JSON media type' do
+ auth_as_mock
+ header 'Accept', 'application/json'
+ get collection_url(:instances)
+ last_response.status.must_equal 200
+ last_response.headers['Content-Type'].must_equal 'application/json'
+ end
+
+ it 'must include the ETag in HTTP headers' do
+ auth_as_mock
+ get collection_url(:instances)
+ last_response.headers['ETag'].wont_be_nil
+ end
+
+ it 'must have the "instances" element on top level' do
+ auth_as_mock
+ get collection_url(:instances)
+ xml_response.root.name.must_equal 'instances'
+ end
+
+ it 'must have some "instance" elements inside "instances"' do
+ auth_as_mock
+ get collection_url(:instances)
+ (xml_response/'instances/instance').wont_be_empty
+ end
+
+ it 'must provide the :id attribute for each instance in collection' do
+ auth_as_mock
+ get collection_url(:instances)
+ (xml_response/'instances/instance').each do |r|
+ r[:id].wont_be_nil
end
+ end
- def test_it_has_unique_ids
- get_auth_url '/api/instances', {}
- ids = []
- (last_xml_response/'instances/instance').each do |image|
- ids << image['id'].to_s
- end
- ids.sort.should == ids.sort.uniq
- end
-
- def test_inst1_has_correct_attributes
- get_auth_url '/api/instances', {}
- instance = (last_xml_response/'instances/instance[@id="inst1"]')
- test_instance_attributes(instance)
- end
-
- def test_it_returns_valid_realm
- get_auth_url '/api/instances/inst1', {}
- instance = (last_xml_response/'instance')
- test_instance_attributes(instance)
- end
-
- def test_it_responses_to_json
- get_auth_url '/api/instances', {}, { :format => :json }
- JSON::parse(last_response.body).class.should == Hash
- JSON::parse(last_response.body)['instances'].class.should == Array
-
- get_auth_url '/api/instances/inst1', {}, { :format => :json }
- last_response.status.should == 200
- JSON::parse(last_response.body).class.should == Hash
- JSON::parse(last_response.body)['instance'].class.should == Hash
- end
-
- def test_it_responses_to_html
- get_auth_url '/api/instances', {}, { :format => :html }
- last_response.status.should == 200
- Nokogiri::HTML(last_response.body).search('html').first.name.should == 'html'
- get_auth_url '/api/instances/inst1', {}, { :format => :html }
- last_response.status.should == 200
- Nokogiri::HTML(last_response.body).search('html').first.name.should == 'html'
- end
-
- def test_it_create_a_new_instance_using_image_id
- params = {
- :image_id => 'img1'
- }
- post_url '/api/instances', params
- last_response.status.should == 201
- last_response.headers['Location'].should_not == nil
- get_auth_url last_response.headers['Location'], {}
- (last_xml_response/'instance/name').should_not == nil
- add_created_instance (last_xml_response/'instance').first['id']
- test_instance_attributes(last_xml_response/'instance')
- end
-
- def test_it_create_a_new_instance_using_image_id_and_name
- params = {
- :image_id => 'img1',
- :name => "unit_test_instance1"
- }
- post_url '/api/instances', params
- last_response.status.should == 201
- last_response.headers['Location'].should_not == nil
- get_auth_url last_response.headers['Location'], {}
- (last_xml_response/'instance/name').text.should == 'unit_test_instance1'
- add_created_instance (last_xml_response/'instance').first['id']
- test_instance_attributes(last_xml_response/'instance')
- end
-
- def test_it_create_a_new_instance_using_image_id_and_name_and_hwp_storage_and_hwp_cpu
- params = {
- :image_id => 'img1',
- :realm_id => '',
- :name => "unit_test_instance3",
- :hwp_id => "m1-large",
- :hwp_storage => '850',
- :hwp_memory => '7680.0',
- :hwp_cpu => "1.0",
- }
- post_url '/api/instances', params
- last_response.status.should == 400
- end
-
- def test_it_create_a_new_instance_using_image_id_and_name_and_hwp_storage
- params = {
- :image_id => 'img1',
- :name => "unit_test_instance2",
- :hwp_id => "m1-small",
- :hwp_storage => "160"
- }
- post_url '/api/instances', params
- last_response.status.should == 201
- last_response.headers['Location'].should_not == nil
- get_auth_url last_response.headers['Location'], {}
- (last_xml_response/'instance/name').text.should == 'unit_test_instance2'
- (last_xml_response/'instance/hardware_profile').first['id'].should == 'm1-small'
- add_created_instance (last_xml_response/'instance').first['id']
- test_instance_attributes(last_xml_response/'instance')
- end
-
- def test_it_z0_stop_and_start_instance
- $created_instances.each do |instance_id|
- get_auth_url "/api/instances/#{instance_id}", {}
- stop_url = (last_xml_response/'actions/link[@rel="stop"]').first['href']
- stop_url.should_not == nil
- post_url stop_url
- last_response.status.should == 200
- instance = Nokogiri::XML(last_response.body)
- test_instance_attributes(instance)
- (instance/'state').text.should == 'STOPPED'
- get_auth_url "/api/instances/#{instance_id}", {}
- start_url = (last_xml_response/'actions/link[@rel="start"]').first['href']
- start_url.should_not == nil
- post_url start_url
- last_response.status.should == 200
- instance = Nokogiri::XML(last_response.body)
- test_instance_attributes(instance)
- (instance/'state').text.should == 'RUNNING'
- end
+ it 'must include the :href attribute for each "instance" element in collection' do
+ auth_as_mock
+ get collection_url(:instances)
+ (xml_response/'instances/instance').each do |r|
+ r[:href].wont_be_nil
end
+ end
- def test_z0_reboot_instance
- $created_instances.each do |instance_id|
- get_auth_url "/api/instances/#{instance_id}", {}
- reboot_url = (last_xml_response/'actions/link[@rel="reboot"]').first['href']
- reboot_url.should_not == nil
- post_url reboot_url
- last_response.status.should == 202
- instance = Nokogiri::XML(last_response.body)
- test_instance_attributes(instance)
- (instance/'state').text.should == 'RUNNING'
- end
+ it 'must use the absolute URL in each :href attribute' do
+ auth_as_mock
+ get collection_url(:instances)
+ (xml_response/'instances/instance').each do |r|
+ r[:href].must_match /^http/
end
+ end
- def test_z1_stop_created_instances
- $created_instances.each do |instance_id|
- get_auth_url "/api/instances/#{instance_id}", {}
- stop_url = (last_xml_response/'actions/link[@rel="stop"]').first['href']
- stop_url.should_not == nil
- post_url stop_url, {}
- last_response.status.should == 200
- instance = Nokogiri::XML(last_response.body)
- test_instance_attributes(instance)
- (instance/'state').text.should == 'STOPPED'
- end
+ it 'must have the URL ending with the :id of the instance' do
+ auth_as_mock
+ get collection_url(:instances)
+ (xml_response/'instances/instance').each do |r|
+ r[:href].must_match /#{r[:id]}$/
end
+ end
- def test_z2_destroy_created_instances
- $created_instances.each do |instance_id|
- get_auth_url "/api/instances/#{instance_id}", {}
- destroy_url = (last_xml_response/'actions/link[@rel="destroy"]').first['href']
- destroy_url.should_not == nil
- delete_url destroy_url, {}
- last_response.status.should == 204
- end
+ it 'must return the list of valid parameters for the :index action' do
+ auth_as_mock
+ options collection_url(:instances) + '/index'
+ last_response.headers['Allow'].wont_be_nil
+ end
+
+ it 'must have the "name" element defined for each instance in collection' do
+ auth_as_mock
+ get collection_url(:instances)
+ (xml_response/'instances/instance').each do |r|
+ (r/'name').wont_be_empty
end
+ end
- def test_create_key_returns_201
- post_url '/api/keys', {:name => Time.now.to_f.to_s}
- last_response.status.should == 201
+ it 'must have the "state" element defined for each instance in collection' do
+ auth_as_mock
+ get collection_url(:instances)
+ (xml_response/'instances/instance').each do |r|
+ (r/'state').wont_be_empty
+ (r/'state').first.must_match /(RUNNING|STOPPED|PENDING)/
end
+ end
- private
+ it 'must return the full "instance" when following the URL in instance element' do
+ auth_as_mock
+ get collection_url(:instances)
+ (xml_response/'instances/instance').each do |r|
+ get collection_url(:instances) + '/' + r[:id]
+ last_response.status.must_equal 200
+ end
+ end
- def test_instance_attributes(instance)
- (instance/'name').should_not == nil
- (instance/'owner_id').should_not == nil
- ['RUNNING', 'STOPPED'].include?((instance/'state').text).should == true
+ it 'must have the "name" element for the instance and it should match with the one in collection' do
+ auth_as_mock
+ get collection_url(:instances)
+ (xml_response/'instances/instance').each do |r|
+ get collection_url(:instances) + '/' + r[:id]
+ (xml_response/'name').wont_be_empty
+ (xml_response/'name').first.text.must_equal((r/'name').first.text)
+ end
+ end
+
+ it 'must have the "name" element for the instance and it should match with the one in collection' do
+ auth_as_mock
+ get collection_url(:instances)
+ (xml_response/'instances/instance').each do |r|
+ get collection_url(:instances) + '/' + r[:id]
+ (xml_response/'state').wont_be_empty
+ (xml_response/'state').first.text.must_equal((r/'state').first.text)
+ end
+ end
+
+ it 'must have the "owner_id" element for the instance and it should match with the one in collection' do
+ auth_as_mock
+ get collection_url(:instances)
+ (xml_response/'instances/instance').each do |r|
+ get collection_url(:instances) + '/' + r[:id]
+ (xml_response/'owner_id').wont_be_empty
+ (xml_response/'owner_id').first.text.must_equal((r/'owner_id').first.text)
+ end
+ end
+
+ it 'must link to the realm that was used to during instance creation' do
+ auth_as_mock
+ get collection_url(:instances)
+ (xml_response/'instances/instance').each do |r|
+ get collection_url(:instances) + '/' + r[:id]
+ (xml_response/'realm').wont_be_empty
+ (xml_response/'realm').size.must_equal 1
+ (xml_response/'realm').first[:id].wont_be_nil
+ (xml_response/'realm').first[:href].wont_be_nil
+ (xml_response/'realm').first[:href].must_match /\/#{(xml_response/'realm').first[:id]}$/
+ end
+ end
- (instance/'public_addreses').should_not == nil
- (instance/'public_addresses/address').to_a.size.should > 0
- (instance/'public_addresses/address').first.text.should_not == ""
- (instance/'public_addresses/address').first[:type].should == "hostname"
+ it 'must link to the image that was used to during instance creation' do
+ auth_as_mock
+ get collection_url(:instances)
+ (xml_response/'instances/instance').each do |r|
+ get collection_url(:instances) + '/' + r[:id]
+ (xml_response/'image').wont_be_empty
+ (xml_response/'image').size.must_equal 1
+ (xml_response/'image').first[:id].wont_be_nil
+ (xml_response/'image').first[:href].wont_be_nil
+ (xml_response/'image').first[:href].must_match /\/#{(xml_response/'image').first[:id]}$/
+ end
+ end
- (instance/'private_addresses').should_not == nil
- (instance/'private_addresses/address').to_a.size.should > 0
- (instance/'private_addresses/address').first.text.should_not == ""
- (instance/'private_addresses/address').first[:type].should == "hostname"
+ it 'must link to the hardware_profile that was used to during instance creation' do
+ auth_as_mock
+ get collection_url(:instances)
+ (xml_response/'instances/instance').each do |r|
+ get collection_url(:instances) + '/' + r[:id]
+ (xml_response/'hardware_profile').wont_be_empty
+ (xml_response/'hardware_profile').size.must_equal 1
+ (xml_response/'hardware_profile').first[:id].wont_be_nil
+ (xml_response/'hardware_profile').first[:href].wont_be_nil
+ (xml_response/'hardware_profile').first[:href].must_match /\/#{(xml_response/'hardware_profile').first[:id]}$/
+ end
+ end
- (instance/'actions/link').to_a.size.should > 0
- (instance/'actions/link').each do |link|
- link['href'].should_not == ""
- link['rel'].should_not == ""
- link['method'].should_not == ""
- ['get', 'post', 'delete', 'put'].include?(link['method']).should == true
+ it 'should advertise the public and private addresses of the instance' do
+ auth_as_mock
+ get collection_url(:instances)
+ (xml_response/'instances/instance').each do |r|
+ get collection_url(:instances) + '/' + r[:id]
+ (xml_response/'public_addresses').wont_be_empty
+ (xml_response/'public_addresses').size.must_equal 1
+ (xml_response/'public_addresses/address').each do |a|
+ a[:type].wont_be_nil
+ a.text.strip.wont_be_empty
end
+ (xml_response/'private_addresses').wont_be_empty
+ (xml_response/'private_addresses').size.must_equal 1
+ (xml_response/'private_addresses/address').each do |a|
+ a[:type].wont_be_nil
+ a.text.strip.wont_be_empty
+ end
+ end
+ end
- (instance/'image').size.should > 0
- (instance/'image').first['href'].should_not == ""
- (instance/'image').first['id'].should_not == ""
- get_auth_url (instance/'image').first['href'], {}
- (last_xml_response/'image').should_not == nil
- (last_xml_response/'image').first['href'] == (instance/'image').first['href']
+ it 'should advertise the storage volumes used by the instance' do
+ auth_as_mock
+ get collection_url(:instances)
+ (xml_response/'instances/instance').each do |r|
+ get collection_url(:instances) + '/' + r[:id]
+ (xml_response/'storage_volumes').wont_be_empty
+ end
+ end
- (instance/'realm').size.should > 0
- (instance/'realm').first['href'].should_not == ""
- (instance/'realm').first['id'].should_not == ""
- get_auth_url (instance/'realm').first['href']
- (last_xml_response/'realm').should_not == nil
- (last_xml_response/'realm').first['href'] == (instance/'realm').first['href']
+ it 'should advertise the list of actions that can be executed for each instance' do
+ auth_as_mock
+ get collection_url(:instances)
+ (xml_response/'instances/instance').each do |r|
+ get collection_url(:instances) + '/' + r[:id]
+ (xml_response/'actions/link').wont_be_empty
+ (xml_response/'actions/link').each do |l|
+ l[:href].wont_be_nil
+ l[:href].must_match /^http/
+ l[:method].wont_be_nil
+ l[:rel].wont_be_nil
+ end
+ end
+ end
- (instance/'hardware_profile').size.should > 0
- (instance/'hardware_profile').first['href'].should_not == ""
- (instance/'hardware_profile').first['id'].should_not == ""
- get_auth_url (instance/'hardware_profile').first['href']
- (last_xml_response/'hardware_profile').should_not == nil
- (last_xml_response/'hardware_profile').first['href'] == (instance/'hardware_profile').first['href']
+ it 'should allow to create and destroy new instance using the first available image without realm' do
+ auth_as_mock
+ get collection_url(:images)
+ image_id = (xml_response/'images/image').first[:id]
+ image_id.wont_be_nil
+ post collection_url(:instances), {
+ :image_id => image_id
+ }
+ last_response.status.must_equal 201 # HTTP_CREATED
+ last_response.headers['Location'].wont_be_nil # Location header must be set, pointing to new the instance
+ instance_id = last_response.headers['Location'].split('/').last
+ # Get the instance and check if ID and image is set correctly
+ get collection_url(:instances) + '/' + instance_id
+ last_response.status.must_equal 200 # HTTP_OK
+ (xml_response/'instance').first[:id].must_equal instance_id
+ (xml_response/'instance/image').first[:id].must_equal image_id
+ # If instance is RUNNING then stop it
+ if (xml_response/'instance/state').first.text == 'RUNNING'
+ post collection_url(:instances) + '/' + instance_id + '/stop'
+ last_response.status.must_equal 202 # HTTP_NO_CONTENT
end
+ # Delete created instance
+ delete collection_url(:instances) + '/' + instance_id
+ last_response.status.must_equal 204 # HTTP_NO_CONTENT
+ end
+ it 'should allow to create and destroy new instance using the first available image within first realm' do
+ auth_as_mock
+ get collection_url(:images)
+ image_id = (xml_response/'images/image').first[:id]
+ get collection_url(:realms)
+ realm_id = (xml_response/'realms/realm').first[:id]
+ image_id.wont_be_nil
+ realm_id.wont_be_nil
+ post collection_url(:instances), {
+ :image_id => image_id,
+ :realm_id => realm_id,
+ }
+ last_response.status.must_equal 201 # HTTP_CREATED
+ last_response.headers['Location'].wont_be_nil # Location header must be set, pointing to new the instance
+ instance_id = last_response.headers['Location'].split('/').last
+ # Get the instance and check if ID and image is set correctly
+ get collection_url(:instances) + '/' + instance_id
+ last_response.status.must_equal 200 # HTTP_OK
+ (xml_response/'instance').first[:id].must_equal instance_id
+ (xml_response/'instance/image').first[:id].must_equal image_id
+ (xml_response/'instance/realm').first[:id].must_equal realm_id
+ # If instance is RUNNING then stop it
+ if (xml_response/'instance/state').first.text == 'RUNNING'
+ post collection_url(:instances) + '/' + instance_id + '/stop'
+ last_response.status.must_equal 202 # HTTP_NO_CONTENT
+ end
+ # Delete created instance
+ delete collection_url(:instances) + '/' + instance_id
+ last_response.status.must_equal 204 # HTTP_NO_CONTENT
end
+
+ it 'should allow to create and destroy new instance using the first available image with user defined name' do
+ auth_as_mock
+ get collection_url(:images)
+ image_id = (xml_response/'images/image').first[:id]
+ image_id.wont_be_nil
+ name = "i#{Time.now.to_i}"
+ post collection_url(:instances), {
+ :image_id => image_id,
+ :name => name
+ }
+ last_response.status.must_equal 201 # HTTP_CREATED
+ last_response.headers['Location'].wont_be_nil # Location header must be set, pointing to new the instance
+ instance_id = last_response.headers['Location'].split('/').last
+ # Get the instance and check if ID and image is set correctly
+ get collection_url(:instances) + '/' + instance_id
+ last_response.status.must_equal 200 # HTTP_OK
+ (xml_response/'instance').first[:id].must_equal instance_id
+ (xml_response/'instance/image').first[:id].must_equal image_id
+ (xml_response/'instance/name').first.text.must_equal name
+ # If instance is RUNNING then stop it
+ if (xml_response/'instance/state').first.text == 'RUNNING'
+ post collection_url(:instances) + '/' + instance_id + '/stop'
+ last_response.status.must_equal 202 # HTTP_NO_CONTENT
+ end
+ # Delete created instance
+ delete collection_url(:instances) + '/' + instance_id
+ last_response.status.must_equal 204 # HTTP_NO_CONTENT
+ end
+
+ it 'should allow to create and destroy new instance using the first available image and first hardware_profile' do
+ auth_as_mock
+ get collection_url(:images)
+ image_id = (xml_response/'images/image').first[:id]
+ get collection_url(:hardware_profiles)
+ hwp_id = (xml_response/'hardware_profiles/hardware_profile').first[:id]
+ image_id.wont_be_nil
+ name = "i#{Time.now.to_i}"
+ post collection_url(:instances), {
+ :image_id => image_id,
+ :hwp_id => hwp_id
+ }
+ last_response.status.must_equal 201 # HTTP_CREATED
+ last_response.headers['Location'].wont_be_nil # Location header must be set, pointing to new the instance
+ instance_id = last_response.headers['Location'].split('/').last
+ # Get the instance and check if ID and image is set correctly
+ get collection_url(:instances) + '/' + instance_id
+ last_response.status.must_equal 200 # HTTP_OK
+ (xml_response/'instance').first[:id].must_equal instance_id
+ (xml_response/'instance/image').first[:id].must_equal image_id
+ (xml_response/'instance/hardware_profile').first[:id].must_equal hwp_id
+ # If instance is RUNNING then stop it
+ if (xml_response/'instance/state').first.text == 'RUNNING'
+ post collection_url(:instances) + '/' + instance_id + '/stop'
+ last_response.status.must_equal 202 # HTTP_NO_CONTENT
+ end
+ # Delete created instance
+ delete collection_url(:instances) + '/' + instance_id
+ last_response.status.must_equal 204 # HTTP_NO_CONTENT
+ end
+
end
diff --git a/server/tests/drivers/mock/keys_test.rb b/server/tests/drivers/mock/keys_test.rb
new file mode 100644
index 0000000..9267b5a
--- /dev/null
+++ b/server/tests/drivers/mock/keys_test.rb
@@ -0,0 +1,158 @@
+describe 'Deltacloud API Keys' do
+ include Deltacloud::Test
+
+ it 'must advertise have the keys collection in API entrypoint' do
+ get API_ROOT_URL
+ (xml_response/'api/link[@rel=keys]').wont_be_empty
+ end
+
+ it 'must require authentication to access the "key" collection' do
+ get collection_url(:keys)
+ last_response.status.must_equal 401
+ end
+
+ it 'should respond with HTTP_OK when accessing the :keys collection with authentication' do
+ auth_as_mock
+ get collection_url(:keys)
+ last_response.status.must_equal 200
+ end
+
+ it 'should support the JSON media type' do
+ auth_as_mock
+ header 'Accept', 'application/json'
+ get collection_url(:keys)
+ last_response.status.must_equal 200
+ last_response.headers['Content-Type'].must_equal 'application/json'
+ end
+
+ it 'must include the ETag in HTTP headers' do
+ auth_as_mock
+ get collection_url(:keys)
+ last_response.headers['ETag'].wont_be_nil
+ end
+
+ it 'must have the "keys" element on top level' do
+ auth_as_mock
+ get collection_url(:keys)
+ xml_response.root.name.must_equal 'keys'
+ end
+
+ it 'must have some "key" elements inside "keys"' do
+ auth_as_mock
+ get collection_url(:keys)
+ (xml_response/'keys/key').wont_be_empty
+ end
+
+ it 'must tell the kind of "key" elements inside "keys"' do
+ auth_as_mock
+ get collection_url(:keys)
+ (xml_response/'keys/key').each do |k|
+ k[:type].must_match /(key|password)/
+ end
+ end
+
+ it 'must provide the :id attribute for each key in collection' do
+ auth_as_mock
+ get collection_url(:keys)
+ (xml_response/'keys/key').each do |r|
+ r[:id].wont_be_nil
+ end
+ end
+
+ it 'must include the :href attribute for each "key" element in collection' do
+ auth_as_mock
+ get collection_url(:keys)
+ (xml_response/'keys/key').each do |r|
+ r[:href].wont_be_nil
+ end
+ end
+
+ it 'must use the absolute URL in each :href attribute' do
+ auth_as_mock
+ get collection_url(:keys)
+ (xml_response/'keys/key').each do |r|
+ r[:href].must_match /^http/
+ end
+ end
+
+ it 'must have the URL ending with the :id of the key' do
+ auth_as_mock
+ get collection_url(:keys)
+ (xml_response/'keys/key').each do |r|
+ r[:href].must_match /#{r[:id]}$/
+ end
+ end
+
+ it 'must return the list of valid parameters for the :index action' do
+ auth_as_mock
+ options collection_url(:keys) + '/index'
+ last_response.headers['Allow'].wont_be_nil
+ end
+
+ it 'must have the "name" element defined for each key in collection' do
+ auth_as_mock
+ get collection_url(:keys)
+ (xml_response/'keys/key').each do |r|
+ (r/'name').wont_be_empty
+ end
+ end
+
+
+ it 'must return the full "key" when following the URL in key element' do
+ auth_as_mock
+ get collection_url(:keys)
+ (xml_response/'keys/key').each do |r|
+ get collection_url(:keys) + '/' + r[:id]
+ last_response.status.must_equal 200
+ end
+ end
+
+ it 'must have the "name" element for the key and it should match with the one in collection' do
+ auth_as_mock
+ get collection_url(:keys)
+ (xml_response/'keys/key').each do |r|
+ get collection_url(:keys) + '/' + r[:id]
+ (xml_response/'name').wont_be_empty
+ (xml_response/'name').first.text.must_equal((r/'name').first.text)
+ end
+ end
+
+ it 'must have the "name" element for the key and it should match with the one in collection' do
+ auth_as_mock
+ get collection_url(:keys)
+ (xml_response/'keys/key').each do |r|
+ get collection_url(:keys) + '/' + r[:id]
+ (xml_response/'state').wont_be_empty
+ (xml_response/'state').first.text.must_equal((r/'state').first.text)
+ end
+ end
+
+ it 'should advertise the list of actions that can be executed for each key' do
+ auth_as_mock
+ get collection_url(:keys)
+ (xml_response/'keys/key').each do |r|
+ get collection_url(:keys) + '/' + r[:id]
+ (xml_response/'actions/link').wont_be_empty
+ (xml_response/'actions/link').each do |l|
+ l[:href].wont_be_nil
+ l[:href].must_match /^http/
+ l[:method].wont_be_nil
+ l[:rel].wont_be_nil
+ end
+ end
+ end
+
+ it 'should allow to create a new key and then remove it' do
+ auth_as_mock
+ key_name = Time.now.to_i.to_s
+ post collection_url(:keys), {
+ :name => 'test_key_'+key_name
+ }
+ last_response.status.must_equal 201 # HTTP_CREATED
+ get collection_url(:keys) + '/' + 'test_key_'+key_name
+ last_response.status.must_equal 200 # HTTP_OK
+ delete collection_url(:keys) + '/' + 'test_key_'+key_name
+ last_response.status.must_equal 204 # HTTP_NO_CONTENT
+ end
+
+end
diff --git a/server/tests/drivers/mock/realms_test.rb b/server/tests/drivers/mock/realms_test.rb
index b0db9e4..6bc9101 100644
--- a/server/tests/drivers/mock/realms_test.rb
+++ b/server/tests/drivers/mock/realms_test.rb
@@ -1,89 +1,129 @@
-# 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.
-#
-
-$:.unshift File.join(File.dirname(__FILE__), '..', '..', '..')
-require 'tests/common'
-
-module DeltacloudUnitTest
- class RealmsTest < Test::Unit::TestCase
- include Rack::Test::Methods
-
- def app
- Sinatra::Application
- end
+describe 'Deltacloud API Realms' do
+ include Deltacloud::Test
- def test_it_returns_realms
- get_auth_url '/api/realms', {}
- (last_xml_response/'realms/realm').length.should > 0
- end
+ it 'must advertise have the realms collection in API entrypoint' do
+ get API_ROOT_URL
+ (xml_response/'api/link[@rel=realms]').wont_be_empty
+ end
+
+ it 'must require authentication to access the "realm" collection' do
+ get collection_url(:realms)
+ last_response.status.must_equal 401
+ end
+
+ it 'should respond with HTTP_OK when accessing the :realms collection with authentication' do
+ auth_as_mock
+ get collection_url(:realms)
+ last_response.status.must_equal 200
+ end
+
+ it 'should support the JSON media type' do
+ auth_as_mock
+ header 'Accept', 'application/json'
+ get collection_url(:realms)
+ last_response.status.must_equal 200
+ last_response.headers['Content-Type'].must_equal 'application/json'
+ end
+
+ it 'must include the ETag in HTTP headers' do
+ auth_as_mock
+ get collection_url(:realms)
+ last_response.headers['ETag'].wont_be_nil
+ end
+
+ it 'must have the "realms" element on top level' do
+ auth_as_mock
+ get collection_url(:realms)
+ xml_response.root.name.must_equal 'realms'
+ end
+
+ it 'must have some "realm" elements inside "realms"' do
+ auth_as_mock
+ get collection_url(:realms)
+ (xml_response/'realms/realm').wont_be_empty
+ end
- def test_it_has_correct_attributes_set
- get_auth_url '/api/realms', {}
- (last_xml_response/'realms/realm').each do |realm|
- realm.attributes.keys.sort.should == [ 'href', 'id' ]
- end
+ it 'must provide the :id attribute for each realm in collection' do
+ auth_as_mock
+ get collection_url(:realms)
+ (xml_response/'realms/realm').each do |r|
+ r[:id].wont_be_nil
end
+ end
- def test_us_has_correct_attributes
- get_auth_url '/api/realms', {}
- realm = (last_xml_response/'realms/realm[@id="us"]')
- test_realm_attributes(realm)
+ it 'must include the :href attribute for each "realm" element in collection' do
+ auth_as_mock
+ get collection_url(:realms)
+ (xml_response/'realms/realm').each do |r|
+ r[:href].wont_be_nil
end
+ end
- def test_it_returns_valid_realm
- get_auth_url '/api/realms/us', {}
- realm = (last_xml_response/'realm')
- test_realm_attributes(realm)
+ it 'must use the absolute URL in each :href attribute' do
+ auth_as_mock
+ get collection_url(:realms)
+ (xml_response/'realms/realm').each do |r|
+ r[:href].must_match /^http/
end
+ end
- def test_it_has_unique_ids
- get_auth_url '/api/realms', {}
- ids = []
- (last_xml_response/'realms/realm').each do |realm|
- ids << realm['id'].to_s
- end
- ids.sort.should == ids.sort.uniq
+ it 'must have the URL ending with the :id of the realm' do
+ auth_as_mock
+ get collection_url(:realms)
+ (xml_response/'realms/realm').each do |r|
+ r[:href].must_match /#{r[:id]}$/
end
+ end
+
+ it 'must return the list of valid parameters for the :index action' do
+ auth_as_mock
+ options collection_url(:realms) + '/index'
+ last_response.headers['Allow'].wont_be_nil
+ end
- def test_it_responses_to_json
- get_auth_url '/api/realms', {}, { :format => :json }
- JSON::parse(last_response.body).class.should == Hash
- JSON::parse(last_response.body)['realms'].class.should == Array
- get_auth_url '/api/realms/us', {}, { :format => :json }
- last_response.status.should == 200
- JSON::parse(last_response.body).class.should == Hash
- JSON::parse(last_response.body)['realm'].class.should == Hash
+ it 'must have the "name" element defined for each realm in collection' do
+ auth_as_mock
+ get collection_url(:realms)
+ (xml_response/'realms/realm').each do |r|
+ (r/'name').wont_be_empty
end
+ end
- def test_it_responses_to_html
- get_auth_url '/api/realms', {}, { :format => :html }
- last_response.status.should == 200
- Nokogiri::HTML(last_response.body).search('html').first.name.should == 'html'
- get_auth_url '/api/realms/us', {}, { :format => :html }
- last_response.status.should == 200
- Nokogiri::HTML(last_response.body).search('html').first.name.should == 'html'
+ it 'must have the "state" element defined for each realm in collection' do
+ auth_as_mock
+ get collection_url(:realms)
+ (xml_response/'realms/realm').each do |r|
+ (r/'state').wont_be_empty
end
+ end
- private
+ it 'must return the full "realm" when following the URL in realm element' do
+ auth_as_mock
+ get collection_url(:realms)
+ (xml_response/'realms/realm').each do |r|
+ get collection_url(:realms) + '/' + r[:id]
+ last_response.status.must_equal 200
+ end
+ end
- def test_realm_attributes(realm)
- (realm/'name').should_not == nil
- (realm/'limit').should_not == nil
- ['AVAILABLE'].include?((realm/'state').text).should == true
+ it 'must have the "name" element for the realm and it should match with the one in collection' do
+ auth_as_mock
+ get collection_url(:realms)
+ (xml_response/'realms/realm').each do |r|
+ get collection_url(:realms) + '/' + r[:id]
+ (xml_response/'name').wont_be_empty
+ (xml_response/'name').first.text.must_equal((r/'name').first.text)
end
+ end
+ it 'must have the "state" element for the realm and it should match with the one in collection' do
+ auth_as_mock
+ get collection_url(:realms)
+ (xml_response/'realms/realm').each do |r|
+ get collection_url(:realms) + '/' + r[:id]
+ (xml_response/'state').wont_be_empty
+ (xml_response/'state').first.text.must_equal((r/'state').first.text)
+ end
end
+
end
diff --git a/server/tests/drivers/mock/setup.rb b/server/tests/drivers/mock/setup.rb
deleted file mode 100644
index 60a7094..0000000
--- a/server/tests/drivers/mock/setup.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-ENV['API_DRIVER'] = "mock"
-ENV['API_USER'] = 'mockuser'
-ENV['API_PASSWORD'] = 'mockpassword'
diff --git a/server/tests/drivers/mock/storage_snapshots_test.rb b/server/tests/drivers/mock/storage_snapshots_test.rb
new file mode 100644
index 0000000..52ea847
--- /dev/null
+++ b/server/tests/drivers/mock/storage_snapshots_test.rb
@@ -0,0 +1,111 @@
+describe 'Deltacloud API storage_snapshots' do
+ include Deltacloud::Test
+
+ it 'must advertise have the storage_snapshots collection in API entrypoint' do
+ get API_ROOT_URL
+ (xml_response/'api/link[@rel=storage_snapshots]').wont_be_empty
+ end
+
+ it 'must require authentication to access the "storage_snapshot" collection' do
+ get collection_url(:storage_snapshots)
+ last_response.status.must_equal 401
+ end
+
+ it 'should respond with HTTP_OK when accessing the :storage_snapshots collection with authentication' do
+ auth_as_mock
+ get collection_url(:storage_snapshots)
+ last_response.status.must_equal 200
+ end
+
+ it 'should support the JSON media type' do
+ auth_as_mock
+ header 'Accept', 'application/json'
+ get collection_url(:storage_snapshots)
+ last_response.status.must_equal 200
+ last_response.headers['Content-Type'].must_equal 'application/json'
+ end
+
+ it 'must include the ETag in HTTP headers' do
+ auth_as_mock
+ get collection_url(:storage_snapshots)
+ last_response.headers['ETag'].wont_be_nil
+ end
+
+ it 'must have the "storage_snapshots" element on top level' do
+ auth_as_mock
+ get collection_url(:storage_snapshots)
+ xml_response.root.name.must_equal 'storage_snapshots'
+ end
+
+ it 'must have some "storage_snapshot" elements inside "storage_snapshots"' do
+ auth_as_mock
+ get collection_url(:storage_snapshots)
+ (xml_response/'storage_snapshots/storage_snapshot').wont_be_empty
+ end
+
+ it 'must provide the :id attribute for each storage_snapshot in collection' do
+ auth_as_mock
+ get collection_url(:storage_snapshots)
+ (xml_response/'storage_snapshots/storage_snapshot').each do |r|
+ r[:id].wont_be_nil
+ end
+ end
+
+ it 'must include the :href attribute for each "storage_snapshot" element in collection' do
+ auth_as_mock
+ get collection_url(:storage_snapshots)
+ (xml_response/'storage_snapshots/storage_snapshot').each do |r|
+ r[:href].wont_be_nil
+ end
+ end
+
+ it 'must use the absolute URL in each :href attribute' do
+ auth_as_mock
+ get collection_url(:storage_snapshots)
+ (xml_response/'storage_snapshots/storage_snapshot').each do |r|
+ r[:href].must_match /^http/
+ end
+ end
+
+ it 'must have the URL ending with the :id of the storage_snapshot' do
+ auth_as_mock
+ get collection_url(:storage_snapshots)
+ (xml_response/'storage_snapshots/storage_snapshot').each do |r|
+ r[:href].must_match /#{r[:id]}$/
+ end
+ end
+
+ it 'must return the list of valid parameters for the :index action' do
+ auth_as_mock
+ options collection_url(:storage_snapshots) + '/index'
+ last_response.headers['Allow'].wont_be_nil
+ end
+
+ it 'must have the "name" element defined for each storage_snapshot in collection' do
+ auth_as_mock
+ get collection_url(:storage_snapshots)
+ (xml_response/'storage_snapshots/storage_snapshot').each do |r|
+ (r/'name').wont_be_empty
+ end
+ end
+
+ it 'must return the full "storage_snapshot" when following the URL in storage_snapshot element' do
+ auth_as_mock
+ get collection_url(:storage_snapshots)
+ (xml_response/'storage_snapshots/storage_snapshot').each do |r|
+ get collection_url(:storage_snapshots) + '/' + r[:id]
+ last_response.status.must_equal 200
+ end
+ end
+
+ it 'must have the "name" element for the storage_snapshot and it should match with the one in collection' do
+ auth_as_mock
+ get collection_url(:storage_snapshots)
+ (xml_response/'storage_snapshots/storage_snapshot').each do |r|
+ get collection_url(:storage_snapshots) + '/' + r[:id]
+ (xml_response/'name').wont_be_empty
+ (xml_response/'name').first.text.must_equal((r/'name').first.text)
+ end
+ end
+
+end
diff --git a/server/tests/drivers/mock/storage_volumes_test.rb b/server/tests/drivers/mock/storage_volumes_test.rb
new file mode 100644
index 0000000..cbafd5d
--- /dev/null
+++ b/server/tests/drivers/mock/storage_volumes_test.rb
@@ -0,0 +1,119 @@
+describe 'Deltacloud API storage_volumes' do
+ include Deltacloud::Test
+
+ it 'must advertise have the storage_volumes collection in API entrypoint' do
+ get API_ROOT_URL
+ (xml_response/'api/link[@rel=storage_volumes]').wont_be_empty
+ end
+
+ it 'must require authentication to access the "storage_volume" collection' do
+ get collection_url(:storage_volumes)
+ last_response.status.must_equal 401
+ end
+
+ it 'should respond with HTTP_OK when accessing the :storage_volumes collection with authentication' do
+ auth_as_mock
+ get collection_url(:storage_volumes)
+ last_response.status.must_equal 200
+ end
+
+ it 'should support the JSON media type' do
+ auth_as_mock
+ header 'Accept', 'application/json'
+ get collection_url(:storage_volumes)
+ last_response.status.must_equal 200
+ last_response.headers['Content-Type'].must_equal 'application/json'
+ end
+
+ it 'must include the ETag in HTTP headers' do
+ auth_as_mock
+ get collection_url(:storage_volumes)
+ last_response.headers['ETag'].wont_be_nil
+ end
+
+ it 'must have the "storage_volumes" element on top level' do
+ auth_as_mock
+ get collection_url(:storage_volumes)
+ xml_response.root.name.must_equal 'storage_volumes'
+ end
+
+ it 'must have some "storage_volume" elements inside "storage_volumes"' do
+ auth_as_mock
+ get collection_url(:storage_volumes)
+ (xml_response/'storage_volumes/storage_volume').wont_be_empty
+ end
+
+ it 'must provide the :id attribute for each storage_volume in collection' do
+ auth_as_mock
+ get collection_url(:storage_volumes)
+ (xml_response/'storage_volumes/storage_volume').each do |r|
+ r[:id].wont_be_nil
+ end
+ end
+
+ it 'must include the :href attribute for each "storage_volume" element in collection' do
+ auth_as_mock
+ get collection_url(:storage_volumes)
+ (xml_response/'storage_volumes/storage_volume').each do |r|
+ r[:href].wont_be_nil
+ end
+ end
+
+ it 'must use the absolute URL in each :href attribute' do
+ auth_as_mock
+ get collection_url(:storage_volumes)
+ (xml_response/'storage_volumes/storage_volume').each do |r|
+ r[:href].must_match /^http/
+ end
+ end
+
+ it 'must have the URL ending with the :id of the storage_volume' do
+ auth_as_mock
+ get collection_url(:storage_volumes)
+ (xml_response/'storage_volumes/storage_volume').each do |r|
+ r[:href].must_match /#{r[:id]}$/
+ end
+ end
+
+ it 'must return the list of valid parameters for the :index action' do
+ auth_as_mock
+ options collection_url(:storage_volumes) + '/index'
+ last_response.headers['Allow'].wont_be_nil
+ end
+
+ it 'must have the "name" element defined for each storage_volume in collection' do
+ auth_as_mock
+ get collection_url(:storage_volumes)
+ (xml_response/'storage_volumes/storage_volume').each do |r|
+ (r/'name').wont_be_empty
+ end
+ end
+
+ it 'must have the "state" element defined for each storage_volume in collection' do
+ auth_as_mock
+ get collection_url(:storage_volumes)
+ (xml_response/'storage_volumes/storage_volume').each do |r|
+ (r/'state').wont_be_empty
+ end
+ end
+
+ it 'must return the full "storage_volume" when following the URL in storage_volume element' do
+ auth_as_mock
+ get collection_url(:storage_volumes)
+ (xml_response/'storage_volumes/storage_volume').each do |r|
+ get collection_url(:storage_volumes) + '/' + r[:id]
+ last_response.status.must_equal 200
+ end
+ end
+
+ it 'must have the "name" element for the storage_volume and it should match with the one in collection' do
+ auth_as_mock
+ get collection_url(:storage_volumes)
+ (xml_response/'storage_volumes/storage_volume').each do |r|
+ get collection_url(:storage_volumes) + '/' + r[:id]
+ (xml_response/'name').wont_be_empty
+ (xml_response/'name').first.text.must_equal((r/'name').first.text)
+ end
+ end
+
+end
diff --git a/server/tests/drivers/mock/url_for_test.rb b/server/tests/drivers/mock/url_for_test.rb
deleted file mode 100644
index ccd4ebf..0000000
--- a/server/tests/drivers/mock/url_for_test.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-# 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.
-#
-
-$:.unshift File.join(File.dirname(__FILE__), '..', '..', '..')
-require 'tests/common'
-
-module DeltacloudUnitTest
- class UrlForTest < Test::Unit::TestCase
- include Rack::Test::Methods
-
- def app
- Sinatra::Application
- end
-
- def test_it_works_for_root
- verify_url_for("/", "/")
- end
-
- def test_it_works_for_root_absolute
- verify_url_for("/", "http://example.org/", :full)
- end
-
- def test_it_works_with_spaces
- verify_url_for("/url with spaces", "/url%20with%20spaces")
- end
-
- def test_it_works_when_given_absolute
- verify_url_for("http://test.com", "http://test.com")
- end
-
- def test_it_works_when_not_at_root_context
- verify_url_for("/", "context/", :path_only, {}, {"SCRIPT_NAME" => "context"})
- end
-
- def verify_url_for(url, expected_url, mode=:path_only, params={}, rack_env={})
- # generate a unique url for each test
- test_url = "/url_for_test/#{expected_url.hash}/#{Time.now.to_i}"
- # Create our sinatra test endpoint
- self.class.create_test_url_content(test_url, url, mode)
-
- # verify the generated url matches what we expect
- get test_url, params, rack_env
- last_response.body.should == expected_url
- end
-
- def self.create_test_url_content(test_url, url_content, mode)
- get test_url do
- content_type "text/plain"
- url_for(url_content, mode)
- end
- end
-
- end
-end
--
1.7.10.1
[PATCH core 08/26] Core: Added sinatra helper to include all Rack and Sinatra middleware
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/lib/sinatra.rb | 21 +++++++++++++++++++++
server/views/storage_snapshots/show.xml.haml | 4 +---
2 files changed, 22 insertions(+), 3 deletions(-)
create mode 100644 server/lib/sinatra.rb
diff --git a/server/lib/sinatra.rb b/server/lib/sinatra.rb
new file mode 100644
index 0000000..6179d55
--- /dev/null
+++ b/server/lib/sinatra.rb
@@ -0,0 +1,21 @@
+# 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.
+
+require_relative 'sinatra/rack_date'
+require_relative 'sinatra/rack_etag'
+require_relative 'sinatra/rack_matrix_params'
+require_relative 'sinatra/rack_driver_select'
+require_relative 'sinatra/accept_media_types'
+require_relative 'sinatra/rack_accept'
diff --git a/server/views/storage_snapshots/show.xml.haml b/server/views/storage_snapshots/show.xml.haml
index c151c1e..a47a652 100644
--- a/server/views/storage_snapshots/show.xml.haml
+++ b/server/views/storage_snapshots/show.xml.haml
@@ -1,9 +1,7 @@
- unless defined?(partial)
!!! XML
%storage_snapshot{:href => storage_snapshot_url(@storage_snapshot.id), :id => @storage_snapshot.id }
+ %name=@storage_snapshot.id
%created<
=@storage_snapshot.created
%storage_volume{:href => storage_volume_url(@storage_snapshot.storage_volume_id), :id => @storage_snapshot.storage_volume_id}
- %actions
- - if driver.respond_to? :destroy_storage_snapshot
- %link{:rel => :destroy, :method => :delete, :href => destroy_storage_snapshot_url(@storage_snapshot.id)}
--
1.7.10.1
[PATCH core 21/26] Core: Fixed couple errors and typos in buckets/blobs code
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/lib/deltacloud/collections/buckets.rb | 84 +++++++++++---------
server/lib/deltacloud/drivers/mock/mock_driver.rb | 6 +-
server/lib/deltacloud/helpers/deltacloud_helper.rb | 2 +
server/lib/deltacloud/helpers/url_helper.rb | 7 +-
server/lib/deltacloud/models/bucket.rb | 4 +
server/views/buckets/new.html.haml | 2 +-
6 files changed, 60 insertions(+), 45 deletions(-)
diff --git a/server/lib/deltacloud/collections/buckets.rb b/server/lib/deltacloud/collections/buckets.rb
index 044bd6a..3a5591d 100644
--- a/server/lib/deltacloud/collections/buckets.rb
+++ b/server/lib/deltacloud/collections/buckets.rb
@@ -18,8 +18,53 @@ module Deltacloud::Collections
check_capability :for => lambda { |m| driver.respond_to? m }
check_features :for => lambda { |c, f| driver.class.has_feature?(c, f) }
+ get route_for('/buckets/new') do
+ respond_to do |format|
+ format.html { haml :"buckets/new" }
+ end
+ end
+
+ get route_for('/buckets/:bucket/%s' % NEW_BLOB_FORM_ID) do
+ @bucket_id = params[:bucket]
+ respond_to do |format|
+ format.html {haml :"blobs/new"}
+ end
+ end
+
collection :buckets do
+ standard_show_operation
+ standard_index_operation
+
+ operation :create, :with_capability => :create_bucket do
+ param :name, :string, :required
+ control do
+ @bucket = driver.create_bucket(credentials, params[:name], params)
+ status 201
+ response['Location'] = bucket_url(@bucket.id)
+ respond_to do |format|
+ format.xml { haml :"buckets/show" }
+ format.json { convert_to_json(:bucket, @bucket) }
+ format.html do
+ redirect bucket_url(@bucket.id) if @bucket and @bucket.id
+ redirect buckets_url
+ end
+ end
+ end
+ end
+
+ operation :destroy, :with_capability => :delete_bucket do
+ control do
+ driver.delete_bucket(credentials, params[:id], params)
+ status 204
+ respond_to do |format|
+ format.xml
+ format.json
+ format.html { redirect(buckets_url) }
+ end
+ end
+ end
+
collection :blobs, :with_id => :blob_id, :no_member => true do
operation :show, :with_capability => :blob do
@@ -115,7 +160,6 @@ module Deltacloud::Collections
end
end
end
-
action :metadata, :http_method => :head, :with_capability => :blob_metadata do
control do
@blob_id = params[:blob]
@@ -168,46 +212,8 @@ module Deltacloud::Collections
end
end
end
-
- end
-
- get route_for('/buckets/new') do
- respond_to do |format|
- format.html { haml :"buckets/new" }
- end
end
- standard_show_operation
- standard_index_operation
-
- operation :create, :with_capability => :create_bucket do
- param :name, :string, :required
- control do
- @bucket = driver.create_bucket(credentials, params[:name], params)
- status 201
- response['Location'] = bucket_url(@bucket.id)
- respond_to do |format|
- format.xml { haml :"buckets/show" }
- format.json { convert_to_json(:bucket, @bucket) }
- format.html do
- redirect bucket_url(@bucket.id) if @bucket and @bucket.id
- redirect buckets_url
- end
- end
- end
- end
-
- operation :destroy, :with_capability => :delete_bucket do
- control do
- driver.delete_bucket(credentials, params[:id], params)
- status 204
- respond_to do |format|
- format.xml
- format.json
- format.html { redirect(buckets_url) }
- end
- end
- end
end
diff --git a/server/lib/deltacloud/drivers/mock/mock_driver.rb b/server/lib/deltacloud/drivers/mock/mock_driver.rb
index 15f06fb..3e2ae5c 100644
--- a/server/lib/deltacloud/drivers/mock/mock_driver.rb
+++ b/server/lib/deltacloud/drivers/mock/mock_driver.rb
@@ -354,7 +354,7 @@ module Deltacloud::Drivers::Mock
check_credentials(credentials)
bucket = bucket(credentials, {:id => name})
raise 'BucketNotExist' if bucket.nil?
- raise "BucketNotEmpty" unless (bucket and bucket.size == "0")
+ raise "BucketNotEmpty" unless bucket.blob_list.empty?
@client.destroy(:buckets, bucket.id)
end
@@ -414,7 +414,7 @@ module Deltacloud::Drivers::Mock
#--
def blob_metadata(credentials, opts={})
check_credentials(credentials)
- if blob = @client.load(:blobs, params[:id])
+ if blob = @client.load(:blobs, opts[:id])
blob[:user_metadata]
else
nil
@@ -427,7 +427,7 @@ module Deltacloud::Drivers::Mock
def update_blob_metadata(credentials, opts={})
check_credentials(credentials)
safely do
- blob = @client.load(:blobs, params[:id])
+ blob = @client.load(:blobs, opts[:id])
return false unless blob
blob[:user_metadata] = BlobHelper::rename_metadata_headers(opts['meta_hash'], '')
@client.store(:blobs, blob)
diff --git a/server/lib/deltacloud/helpers/deltacloud_helper.rb b/server/lib/deltacloud/helpers/deltacloud_helper.rb
index 83741f9..ee9decc 100644
--- a/server/lib/deltacloud/helpers/deltacloud_helper.rb
+++ b/server/lib/deltacloud/helpers/deltacloud_helper.rb
@@ -230,6 +230,8 @@ module Deltacloud::Helpers
end
end
+ NEW_BLOB_FORM_ID = 'new_blob_form_d15cfd90'
+
def new_blob_form_url(bucket)
bucket_url(@bucket.name) + "/" + NEW_BLOB_FORM_ID
end
diff --git a/server/lib/deltacloud/helpers/url_helper.rb b/server/lib/deltacloud/helpers/url_helper.rb
index 87bc93e..fd71f43 100644
--- a/server/lib/deltacloud/helpers/url_helper.rb
+++ b/server/lib/deltacloud/helpers/url_helper.rb
@@ -33,8 +33,11 @@ module Sinatra
if name.to_s =~ /^([\w\_]+)_url$/
if args.size > 0
t = $1
- if t =~ /^(stop|reboot|destroy|start|attach|detach)_/
- api_url_for(t.pluralize.split('_').last + '/' + args.first + '/' + $1, :full)
+ if t.match(/^(stop|reboot|start|attach|detach)_/)
+ action = $1
+ api_url_for(t.pluralize.split('_').last + '/' + args.first + '/' + action, :full)
+ elsif t.match(/^(destroy|update)_/)
+ api_url_for(t.pluralize.split('_').last + '/' + args.first, :full)
else
api_url_for(t.pluralize, :full) + '/' + "#{args.first}"
end
diff --git a/server/lib/deltacloud/models/bucket.rb b/server/lib/deltacloud/models/bucket.rb
index b1551b7..0c21c21 100644
--- a/server/lib/deltacloud/models/bucket.rb
+++ b/server/lib/deltacloud/models/bucket.rb
@@ -22,6 +22,10 @@ class Bucket < BaseModel
alias :to_hash_original :to_hash
+ def blob_list
+ @blob_list || []
+ end
+
def to_hash
h = self.to_hash_original
unless blob_list.nil?
diff --git a/server/views/buckets/new.html.haml b/server/views/buckets/new.html.haml
index 2abaa96..bc0f0c3 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_has_feature?(:bucket_location, :buckets)
+ -if driver.class.has_feature?(:bucket_location, :buckets)
%p
%label
Location: (optional)
--
1.7.10.1
[PATCH core 23/26] Core: Added the minitest gem to the Gemfile for MRI 1.8
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/Gemfile | 1 +
1 file changed, 1 insertion(+)
diff --git a/server/Gemfile b/server/Gemfile
index 1750bdd..244814c 100644
--- a/server/Gemfile
+++ b/server/Gemfile
@@ -10,5 +10,6 @@ group :development do
gem "ci_reporter"
gem "cucumber", ">= 0.6.3"
gem "rspec", ">= 2.0.0"
+ gem "minitest"
end
--
1.7.10.1
[PATCH core 10/26] Core: Renamed @profiles to @hardware_profiles to make standard operation helpers work
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/views/hardware_profiles/index.html.haml | 2 +-
server/views/hardware_profiles/index.xml.haml | 4 ++--
server/views/hardware_profiles/show.html.haml | 6 +++---
server/views/hardware_profiles/show.xml.haml | 6 +++---
4 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/server/views/hardware_profiles/index.html.haml b/server/views/hardware_profiles/index.html.haml
index 64e3455..59d43eb 100644
--- a/server/views/hardware_profiles/index.html.haml
+++ b/server/views/hardware_profiles/index.html.haml
@@ -3,7 +3,7 @@
%div{ :'data-role' => :content, :'data-theme' => 'c'}
%ul{ :'data-role' => :listview, :'data-inset' => :true }
- - for profile in @profiles
+ - for profile in @hardware_profiles
%li{ :'data-theme' => 'c'}
%a{ :href => hardware_profile_url(profile.name)}
%img{ :class => 'ui-link-thumb', :src => '/images/profile.png'}
diff --git a/server/views/hardware_profiles/index.xml.haml b/server/views/hardware_profiles/index.xml.haml
index cf0a69f..25e0496 100644
--- a/server/views/hardware_profiles/index.xml.haml
+++ b/server/views/hardware_profiles/index.xml.haml
@@ -1,4 +1,4 @@
!!! XML
%hardware_profiles
- - @profiles.each do |prof|
- = haml :'hardware_profiles/show', :locals => { :@profile => prof, :partial => true }
+ - @hardware_profiles.each do |prof|
+ = haml :'hardware_profiles/show', :locals => { :@hardware_profile => prof, :partial => true }
diff --git a/server/views/hardware_profiles/show.html.haml b/server/views/hardware_profiles/show.html.haml
index c27690e..033e94d 100644
--- a/server/views/hardware_profiles/show.html.haml
+++ b/server/views/hardware_profiles/show.html.haml
@@ -1,12 +1,12 @@
=header "Hardware profiles"
-=subheader @profile.name
+=subheader @hardware_profile.name
%div{ :'data-role' => :content, :'data-theme' => 'c'}
%ul{ :'data-role' => :listview , :'data-inset' => :true, :'data-divider-theme' => 'd'}
%li{ :'data-role' => 'list-divider'} Name
%li
- %p{ :'data-role' => 'fieldcontain'}=@profile.name
- - @profile.each_property do |p|
+ %p{ :'data-role' => 'fieldcontain'}=@hardware_profile.name
+ - @hardware_profile.each_property do |p|
%li{ :'data-role' => 'list-divider'} #{p.name.to_s.titlecase}
%li
%p{ :'data-role' => 'fieldcontain'}
diff --git a/server/views/hardware_profiles/show.xml.haml b/server/views/hardware_profiles/show.xml.haml
index 094ffd5..21e488c 100644
--- a/server/views/hardware_profiles/show.xml.haml
+++ b/server/views/hardware_profiles/show.xml.haml
@@ -1,9 +1,9 @@
- unless defined?(partial)
!!! XML
-%hardware_profile{ :href => hardware_profile_url(@profile.name), :id => @profile.name }
+%hardware_profile{ :href => hardware_profile_url(@hardware_profile.name), :id => @hardware_profile.name }
%name<
- =@profile.name
- - @profile.each_property do |prop|
+ = @hardware_profile.name
+ - @hardware_profile.each_property do |prop|
- attr = { :name => prop.name, :kind => prop.kind, :unit => prop.unit }
- if prop.kind == :fixed
%property{ attr, :value => prop.value }/
--
1.7.10.1
[PATCH core 18/26] Core: Backported the 'metrics' collection from current Deltacloud
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/lib/deltacloud/collections/metrics.rb | 27 ++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
create mode 100644 server/lib/deltacloud/collections/metrics.rb
diff --git a/server/lib/deltacloud/collections/metrics.rb b/server/lib/deltacloud/collections/metrics.rb
new file mode 100644
index 0000000..acc951d
--- /dev/null
+++ b/server/lib/deltacloud/collections/metrics.rb
@@ -0,0 +1,27 @@
+# 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::Collections
+ class Metrics < Base
+
+ collection :metrics do
+ description 'Metrics provide monitoring for the cloud resources'
+
+ standard_index_operation
+ standard_show_operation
+
+ end
+ end
+end
--
1.7.10.1
[PATCH core 15/26] Core: Added the Deltacloud library wrapper (require 'lib/deltacloud')
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
This will make possible to use Deltacloud drivers API with thirty-party
application whithout spawning a server.
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/lib/deltacloud.rb | 64 ++++++++++++++++++++++++++++++++++++++--------
1 file changed, 54 insertions(+), 10 deletions(-)
diff --git a/server/lib/deltacloud.rb b/server/lib/deltacloud.rb
index 6ff547e..d64797d 100644
--- a/server/lib/deltacloud.rb
+++ b/server/lib/deltacloud.rb
@@ -1,4 +1,3 @@
-#
# 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
@@ -14,14 +13,59 @@
# License for the specific language governing permissions and limitations
# under the License.
-require 'deltacloud/drivers'
+require 'rubygems'
+
+unless Kernel.respond_to?(:require_relative)
+ module Kernel
+ def require_relative(path)
+ require File.join(File.dirname(caller[0]), path.to_str)
+ end
+ end
+end
+
+require 'ostruct'
+
+require_relative 'deltacloud/core_ext/string'
+require_relative 'deltacloud/core_ext/array'
+require_relative 'deltacloud/core_ext/hash'
+require_relative 'deltacloud/core_ext/integer'
+require_relative 'deltacloud/core_ext/proc'
+require_relative 'deltacloud/models'
+require_relative 'deltacloud/drivers'
+require_relative 'deltacloud/helpers/driver_helper'
+
+module Deltacloud
+
+ def self.drivers
+ Drivers.driver_config
+ end
+
+ class Library
+ include Helpers::Drivers
+ attr_reader :backend, :credentials
+
+ def initialize(driver_name, opts={}, &block)
+ Thread.current[:driver] = driver_name.to_s
+ Thread.current[:provider] = opts[:provider] if opts[:provider]
+ @backend = driver
+ @credentials = OpenStruct.new(:user => opts[:user], :password => opts[:password])
+ yield self if block_given?
+ end
+
+ def method_missing(name, *args)
+ return super unless backend.respond_to? name
+ begin
+ params = ([@credentials] + args).flatten
+ backend.send(name, *params)
+ rescue ArgumentError
+ backend.send(name, *args)
+ end
+ end
+
+ end
-require 'deltacloud/core_ext'
+ def self.new(driver_name, opts={}, &block)
+ Library.new(driver_name, opts, &block)
+ end
-require 'deltacloud/base_driver'
-require 'deltacloud/hardware_profile'
-require 'deltacloud/state_machine'
-require 'deltacloud/helpers'
-require 'deltacloud/models'
-require 'deltacloud/validation'
-require 'deltacloud/runner'
+end
--
1.7.10.1
RE: [PATCH core 11/26] Core: Storage volumes should always show the :name attribute
Posted by "Koper, Dies" <di...@fast.au.fujitsu.com>.
Hi Michal,
This is the commit I was referring to on irc.
The patch description does not match your change in show.html.haml.
If I take the line out, the GUI page looks much better.
Regards,
Dies Koper
> -----Original Message-----
> From: mfojtik@redhat.com [mailto:mfojtik@redhat.com]
> Sent: Wednesday, 16 May 2012 1:16 AM
> To: dev@deltacloud.apache.org
> Subject: [PATCH core 11/26] Core: Storage volumes should always show
> the :name attribute
>
> From: Michal Fojtik <mf...@redhat.com>
>
>
> Signed-off-by: Michal fojtik <mf...@redhat.com>
> ---
> server/views/storage_volumes/show.html.haml | 1 +
> server/views/storage_volumes/show.xml.haml | 5 ++---
> 2 files changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/server/views/storage_volumes/show.html.haml
> b/server/views/storage_volumes/show.html.haml
> index aabdd85..1c1f458 100644
> --- a/server/views/storage_volumes/show.html.haml
> +++ b/server/views/storage_volumes/show.html.haml
> @@ -4,6 +4,7 @@
> %div{ :'data-role' => :content, :'data-theme' => 'c'}
> %ul{ :'data-role' => :listview , :'data-inset' => :true,
:'data-divider-theme'
> => 'd'}
> %li{ :'data-role' => 'list-divider'} Name
> + %p{ :'data-role' => 'fieldcontain'}=@storage_volume.id
> %li
> %p{ :'data-role' => 'fieldcontain'}=@storage_volume.id
> %li{ :'data-role' => 'list-divider'} Created
> diff --git a/server/views/storage_volumes/show.xml.haml
> b/server/views/storage_volumes/show.xml.haml
> index 440609b..f9f5f25 100644
> --- a/server/views/storage_volumes/show.xml.haml
> +++ b/server/views/storage_volumes/show.xml.haml
> @@ -9,9 +9,8 @@
> - if @storage_volume.kind
> %kind<
> = @storage_volume.kind
> - - if @storage_volume.name
> - %name<
> - = @storage_volume.name
> + %name<
> + = @storage_volume.name || @storage_volume.id
> - if @storage_volume.device
> %device<
> = @storage_volume.device
> --
> 1.7.10.1
>
[PATCH core 11/26] Core: Storage volumes should always show the :name attribute
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/views/storage_volumes/show.html.haml | 1 +
server/views/storage_volumes/show.xml.haml | 5 ++---
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/server/views/storage_volumes/show.html.haml b/server/views/storage_volumes/show.html.haml
index aabdd85..1c1f458 100644
--- a/server/views/storage_volumes/show.html.haml
+++ b/server/views/storage_volumes/show.html.haml
@@ -4,6 +4,7 @@
%div{ :'data-role' => :content, :'data-theme' => 'c'}
%ul{ :'data-role' => :listview , :'data-inset' => :true, :'data-divider-theme' => 'd'}
%li{ :'data-role' => 'list-divider'} Name
+ %p{ :'data-role' => 'fieldcontain'}=@storage_volume.id
%li
%p{ :'data-role' => 'fieldcontain'}=@storage_volume.id
%li{ :'data-role' => 'list-divider'} Created
diff --git a/server/views/storage_volumes/show.xml.haml b/server/views/storage_volumes/show.xml.haml
index 440609b..f9f5f25 100644
--- a/server/views/storage_volumes/show.xml.haml
+++ b/server/views/storage_volumes/show.xml.haml
@@ -9,9 +9,8 @@
- if @storage_volume.kind
%kind<
= @storage_volume.kind
- - if @storage_volume.name
- %name<
- = @storage_volume.name
+ %name<
+ = @storage_volume.name || @storage_volume.id
- if @storage_volume.device
%device<
= @storage_volume.device
--
1.7.10.1
[PATCH core 04/26] Core: Drivers are now loaded on demand and the 'require "base_driver"' is not longer required in driver
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/lib/deltacloud/drivers.rb | 48 +------
.../lib/deltacloud/drivers/azure/azure_driver.rb | 4 -
server/lib/deltacloud/drivers/base_driver.rb | 7 +-
.../lib/deltacloud/drivers/condor/condor_driver.rb | 7 -
server/lib/deltacloud/drivers/ec2/ec2_driver.rb | 15 +--
.../drivers/eucalyptus/eucalyptus_driver.rb | 4 -
server/lib/deltacloud/drivers/features.rb | 111 ++++++++++++++++
.../lib/deltacloud/drivers/gogrid/gogrid_driver.rb | 6 -
.../lib/deltacloud/drivers/google/google_driver.rb | 3 -
server/lib/deltacloud/drivers/mock/mock_client.rb | 11 --
server/lib/deltacloud/drivers/mock/mock_driver.rb | 21 +--
.../drivers/mock/mock_driver_cimi_methods.rb | 139 --------------------
.../drivers/opennebula/opennebula_driver.rb | 4 -
.../drivers/openstack/openstack_driver.rb | 6 +-
.../drivers/rackspace/rackspace_driver.rb | 9 +-
.../lib/deltacloud/drivers/rhevm/rhevm_driver.rb | 17 +--
.../drivers/rimuhosting/rimuhosting_client.rb | 17 ++-
.../drivers/rimuhosting/rimuhosting_driver.rb | 7 +-
.../drivers/terremark/terremark_driver.rb | 3 +-
.../deltacloud/drivers/vsphere/vsphere_driver.rb | 11 +-
server/lib/deltacloud/models/state_machine.rb | 19 ++-
21 files changed, 171 insertions(+), 298 deletions(-)
create mode 100644 server/lib/deltacloud/drivers/features.rb
diff --git a/server/lib/deltacloud/drivers.rb b/server/lib/deltacloud/drivers.rb
index dfc998d..14e7ee0 100644
--- a/server/lib/deltacloud/drivers.rb
+++ b/server/lib/deltacloud/drivers.rb
@@ -13,25 +13,15 @@
# License for the specific language governing permissions and limitations
# under the License.
-module Deltacloud
+require_relative 'drivers/exceptions'
+require_relative 'drivers/base_driver'
+require_relative 'drivers/features'
+require 'yaml'
+module Deltacloud
module Drivers
- require 'yaml'
-
- DEFAULT_COLLECTIONS = [
- :hardware_profiles,
- :images,
- :instances,
- :instance_states,
- :realms,
- :storage_volumes,
- :storage_snapshots
- ]
-
- DRIVER=ENV['API_DRIVER'] ? ENV['API_DRIVER'].to_sym : :mock
-
- def driver_config
+ def self.driver_config
if Thread::current[:drivers].nil?
Thread::current[:drivers] = {}
top_srcdir = File.join(File.dirname(__FILE__), '..', '..')
@@ -42,31 +32,5 @@ module Deltacloud
Thread::current[:drivers]
end
- def driver_symbol
- (Thread.current[:driver] || DRIVER).to_sym
- end
-
- def driver_name
- driver_config[:"#{driver_symbol}"][:name]
- end
-
- def driver_class
- basename = driver_config[:"#{driver_symbol}"][:class] || "#{driver_name}Driver"
- Deltacloud::Drivers.const_get(driver_name).const_get(basename)
- end
-
- def driver_source_name
- File.join("deltacloud", "drivers", "#{driver_symbol}", "#{driver_symbol}_driver.rb")
- end
-
- def driver_mock_source_name
- return File.join('deltacloud', 'drivers', "#{driver_symbol}",
- "#{driver_symbol}_driver.rb") if driver_name.eql? 'Mock'
- end
-
- def driver
- require driver_source_name
- @driver ||= driver_class.new
- end
end
end
diff --git a/server/lib/deltacloud/drivers/azure/azure_driver.rb b/server/lib/deltacloud/drivers/azure/azure_driver.rb
index 24feeb7..db2b6c6 100644
--- a/server/lib/deltacloud/drivers/azure/azure_driver.rb
+++ b/server/lib/deltacloud/drivers/azure/azure_driver.rb
@@ -15,7 +15,6 @@
# under the License.
#Windows Azure (WAZ) gem at http://github.com/johnnyhalife/waz-storage
-require 'deltacloud/base_driver'
require 'waz-blobs'
module Deltacloud
@@ -24,9 +23,6 @@ module Deltacloud
class AzureDriver < Deltacloud::BaseDriver
- def supported_collections; [:buckets]
- end
-
#--
# Buckets
#--
diff --git a/server/lib/deltacloud/drivers/base_driver.rb b/server/lib/deltacloud/drivers/base_driver.rb
index 5fb1a79..f3637b7 100644
--- a/server/lib/deltacloud/drivers/base_driver.rb
+++ b/server/lib/deltacloud/drivers/base_driver.rb
@@ -20,6 +20,11 @@ module Deltacloud
include ExceptionHandler
+ STATE_MACHINE_OPTS = {
+ :all_states => [:start, :pending, :running, :stopping, :stopped, :finish],
+ :all_actions => [:create, :reboot, :stop, :start, :destroy]
+ }
+
def self.driver_name
name.split('::').last.gsub('Driver', '').downcase
end
@@ -114,7 +119,7 @@ module Deltacloud
end
def self.define_instance_states(&block)
- machine = ::Deltacloud::StateMachine.new(&block)
+ machine = ::Deltacloud::StateMachine.new(STATE_MACHINE_OPTS, &block)
@instance_state_machine = machine
end
diff --git a/server/lib/deltacloud/drivers/condor/condor_driver.rb b/server/lib/deltacloud/drivers/condor/condor_driver.rb
index f5cb741..b0564f9 100644
--- a/server/lib/deltacloud/drivers/condor/condor_driver.rb
+++ b/server/lib/deltacloud/drivers/condor/condor_driver.rb
@@ -14,9 +14,6 @@
# under the License.
#
-require 'deltacloud/base_driver'
-
-
class Instance
def self.convert_condor_state(state_id)
case state_id
@@ -44,10 +41,6 @@ module Deltacloud
feature :instances, :user_data
feature :instances, :authentication_password
- def supported_collections
- DEFAULT_COLLECTIONS - [ :storage_volumes, :storage_snapshots ]
- end
-
CONDOR_MAPPER_DIR = ENV['CONDOR_MAPPER_DIR'] || '/var/tmp'
def hardware_profiles(credentials, opts={})
diff --git a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
index 808b2b2..8777807 100644
--- a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
+++ b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
@@ -14,7 +14,6 @@
# under the License.
#
-require 'deltacloud/base_driver'
require 'aws'
class Instance
@@ -29,13 +28,8 @@ end
module Deltacloud
module Drivers
- module EC2
- class EC2Driver < Deltacloud::BaseDriver
-
- def supported_collections
-
- DEFAULT_COLLECTIONS + [ :keys, :buckets, :load_balancers, :addresses, :firewalls, :metrics ]
- end
+ module Ec2
+ class Ec2Driver < Deltacloud::BaseDriver
feature :instances, :user_data
feature :instances, :authentication_key
@@ -444,9 +438,7 @@ module Deltacloud
safely do
s3_bucket = s3_client.bucket(opts['bucket'])
if(opts[:id])
- s3_key = s3_bucket.key(opts[:id], true)
- raise "Blob #{opts[:id]} in Bucket #{opts['bucket']} NotFound" unless s3_key.exists?
- blobs << convert_object(s3_key)
+ blobs << convert_object(s3_bucket.key(opts[:id], true))
else
s3_bucket.keys({}, true).each do |s3_object|
blobs << convert_object(s3_object)
@@ -502,7 +494,6 @@ module Deltacloud
blob_meta = {}
safely do
the_blob = s3_client.bucket(opts['bucket']).key(opts[:id], true)
- raise "Blob #{opts[:id]} in Bucket #{opts['bucket']} NotFound" unless the_blob.exists?
blob_meta = the_blob.meta_headers
end
end
diff --git a/server/lib/deltacloud/drivers/eucalyptus/eucalyptus_driver.rb b/server/lib/deltacloud/drivers/eucalyptus/eucalyptus_driver.rb
index 2270178..54c1eed 100644
--- a/server/lib/deltacloud/drivers/eucalyptus/eucalyptus_driver.rb
+++ b/server/lib/deltacloud/drivers/eucalyptus/eucalyptus_driver.rb
@@ -21,10 +21,6 @@ module Deltacloud
module Eucalyptus
class EucalyptusDriver < EC2::EC2Driver
- def supported_collections
- DEFAULT_COLLECTIONS + [ :keys, :buckets, :addresses, :firewalls ]
- end
-
feature :instances, :user_data
feature :instances, :authentication_key
feature :instances, :firewalls
diff --git a/server/lib/deltacloud/drivers/features.rb b/server/lib/deltacloud/drivers/features.rb
new file mode 100644
index 0000000..01d8656
--- /dev/null
+++ b/server/lib/deltacloud/drivers/features.rb
@@ -0,0 +1,111 @@
+# 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
+ module InstanceFeatures
+
+ def self.included(k)
+ current_features = features
+ k.instance_eval do
+ features(¤t_features)
+ end
+ end
+
+ def self.features(&block)
+ block_given? ? @features = block : @features || Proc.new{}
+ end
+
+ features do
+
+ feature :user_name, :for => :instances do
+ description "Allow to set user-defined name for the instance"
+ operation :create do
+ param :name, :string, :optional
+ end
+ end
+
+ feature :user_data, :for => :instances do
+ description "Allow to pass user-defined data into the instance"
+ operation :create do
+ param :user_data, :string, :optional
+ end
+ end
+
+ feature :user_iso, :for => :instances do
+ description "Base64 encoded gzipped ISO file will be accessible as CD-ROM drive in instance"
+ operation :create do
+ param :user_iso, :string, :optional
+ end
+ end
+
+ feature :firewalls, :for => :instances do
+ description "Put instance in one or more firewalls (security groups) on launch"
+ operation :create do
+ param :firewalls, :array, :optional, nil, "Array of firewall ID strings"
+ "Array of firewall (security group) id"
+ end
+ end
+
+ feature :authentication_key, :for => :instances do
+ operation :create do
+ param :keyname, :string, :optional, [], "Key authentification method"
+ end
+ operation :show do
+ end
+ end
+
+ feature :authentication_password, :for => :instances do
+ operation :create do
+ param :password, :string, :optional
+ end
+ end
+
+ feature :hardware_profiles, :for => :instances do
+ description "Size instances according to changes to a hardware profile"
+ # The parameters are filled in from the hardware profiles
+ end
+
+ feature :register_to_load_balancer, :for => :instances do
+ description "Register instance to load balancer"
+ operation :create do
+ param :load_balancer_id, :string, :optional
+ end
+ end
+
+ feature :instance_count, :for => :instances do
+ description "Number of instances to be launch with at once"
+ operation :create do
+ param :instance_count, :string, :optional
+ end
+ end
+
+ feature :attach_snapshot, :for => :instances do
+ description "Attach an snapshot to instance on create"
+ operation :create do
+ param :snapshot_id, :string, :optional
+ param :device_name, :string, :optional
+ end
+ end
+
+ feature :sandboxing, :for => :instances do
+ description "Allow lanuching sandbox images"
+ operation :create do
+ param :sandbox, :string, :optional
+ end
+ end
+ end
+
+ end
+end
diff --git a/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb b/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb
index 285f58f..2a83c24 100644
--- a/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb
+++ b/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb
@@ -14,7 +14,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-require 'deltacloud/base_driver'
require 'deltacloud/drivers/gogrid/gogrid_client'
class Instance
@@ -61,11 +60,6 @@ class GogridDriver < Deltacloud::BaseDriver
@hardware_profiles
end
- def supported_collections
- DEFAULT_COLLECTIONS.reject! { |c| [ :storage_volumes, :storage_snapshots ].include?(c) }
- DEFAULT_COLLECTIONS + [ :keys, :load_balancers ]
- end
-
def images(credentials, opts=nil)
imgs = []
if opts and opts[:id]
diff --git a/server/lib/deltacloud/drivers/google/google_driver.rb b/server/lib/deltacloud/drivers/google/google_driver.rb
index 2ffb5f8..8bc6f25 100644
--- a/server/lib/deltacloud/drivers/google/google_driver.rb
+++ b/server/lib/deltacloud/drivers/google/google_driver.rb
@@ -21,9 +21,6 @@ module Deltacloud
class GoogleDriver < Deltacloud::BaseDriver
- def supported_collections; [:buckets]
- end
-
feature :buckets, :bucket_location
#--
diff --git a/server/lib/deltacloud/drivers/mock/mock_client.rb b/server/lib/deltacloud/drivers/mock/mock_client.rb
index 956df44..4c27a66 100644
--- a/server/lib/deltacloud/drivers/mock/mock_client.rb
+++ b/server/lib/deltacloud/drivers/mock/mock_client.rb
@@ -91,17 +91,6 @@ module Deltacloud::Drivers::Mock
FileUtils.rm(fname) if File::exists?(fname)
end
- def store_cimi(collection, obj)
- raise "Why no obj.name?" unless obj.name
- File::open(cimi_file(collection, obj.name), "w") { |f| f.write(obj.to_json) }
- end
-
- def destroy_cimi(collection, id)
- fname = cimi_file(collection, id)
- raise "No such object: #{id} in #{collection} collection" unless File::exists?(fname)
- FileUtils.rm(fname)
- end
-
def load_all_cimi(model_name)
model_files = Dir[File::join(cimi_dir(model_name), "*.json")]
model_files.map{|f| File.read(f)}
diff --git a/server/lib/deltacloud/drivers/mock/mock_driver.rb b/server/lib/deltacloud/drivers/mock/mock_driver.rb
index b58872c..15f06fb 100644
--- a/server/lib/deltacloud/drivers/mock/mock_driver.rb
+++ b/server/lib/deltacloud/drivers/mock/mock_driver.rb
@@ -14,28 +14,16 @@
# License for the specific language governing permissions and limitations
# under the License.
-
require 'yaml'
require 'base64'
require 'etc'
-require 'deltacloud/base_driver'
-require 'deltacloud/drivers/mock/mock_client'
-require 'deltacloud/drivers/mock/mock_driver_cimi_methods'
+require_relative 'mock_client'
+require_relative 'mock_driver_cimi_methods'
module Deltacloud::Drivers::Mock
class MockDriver < Deltacloud::BaseDriver
- # If the provider is set to storage, pretend to be a storage-only
- # driver
- def supported_collections
- if api_provider == 'storage'
- [:buckets]
- else
- DEFAULT_COLLECTIONS + [:buckets, :keys]
- end
- end
-
( REALMS = [
Realm.new({
:id=>'us',
@@ -299,11 +287,6 @@ module Deltacloud::Drivers::Mock
snapshots
end
- def destroy_storage_snapshot(credentials, opts={})
- check_credentials(credentials)
- @client.destroy(:storage_snapshots, opts[:id])
- end
-
def keys(credentials, opts={})
check_credentials(credentials)
result = @client.build_all(Key)
diff --git a/server/lib/deltacloud/drivers/mock/mock_driver_cimi_methods.rb b/server/lib/deltacloud/drivers/mock/mock_driver_cimi_methods.rb
index 2dec66b..bebc45c 100644
--- a/server/lib/deltacloud/drivers/mock/mock_driver_cimi_methods.rb
+++ b/server/lib/deltacloud/drivers/mock/mock_driver_cimi_methods.rb
@@ -32,49 +32,6 @@ module Deltacloud::Drivers::Mock
end
end
- def create_network(credentials, opts={})
- check_credentials(credentials)
- id = "#{opts[:env].send("networks_url")}/#{opts[:name]}"
- net_hsh = { "id"=> id,
- "name" => opts[:name],
- "description" => opts[:description],
- "created" => Time.now,
- "state" => "STARTED",
- "access" => opts[:network_config].access,
- "bandwithLimit" => opts[:network_config].bandwidth_limit,
- "trafficPriority" => opts[:network_config].traffic_priority,
- "maxTrafficDelay" => opts[:network_config].max_traffic_delay,
- "maxTrafficLoss" =>opts[:network_config].max_traffic_loss,
- "maxTrafficJitter" =>opts[:network_config].max_traffic_jitter,
- "routingGroup"=> { "href" => opts[:routing_group].id },
- "operations" => [{"rel"=>"edit", "href"=> id},
- {"rel"=>"delete", "href"=> id}] }
- network = CIMI::Model::Network.from_json(JSON.generate(net_hsh))
-
- @client.store_cimi(:network, network)
- network
- end
-
- def delete_network(credentials, id)
- check_credentials(credentials)
- @client.destroy_cimi(:network, id)
- end
-
- def start_network(credentials, id)
- check_credentials(credentials)
- update_object_state(id, "Network", "STARTED")
- end
-
- def stop_network(credentials, id)
- check_credentials(credentials)
- update_object_state(id, "Network", "STOPPED")
- end
-
- def suspend_network(credentials, id)
- check_credentials(credentials)
- update_object_state(id, "Network", "SUSPENDED")
- end
-
def network_configurations(credentials, opts={})
check_credentials(credentials)
if opts[:id].nil?
@@ -130,43 +87,6 @@ module Deltacloud::Drivers::Mock
end
end
- def create_vsp(credentials, opts={})
- check_credentials(credentials)
- id = "#{opts[:env].send("vsps_url")}/#{opts[:name]}"
- vsp_hash = { "id" => id,
- "name" => opts[:name],
- "description" => opts[:description],
- "state" => "STARTED",
- "created" => Time.now,
- "bandwidthReservation"=>opts[:vsp_config].bandwidth_reservation,
- "trafficPriority"=>opts[:vsp_config].traffic_priority,
- "maxTrafficDelay"=>opts[:vsp_config].max_traffic_delay,
- "maxTrafficLoss"=>opts[:vsp_config].max_traffic_loss,
- "maxTrafficJitter"=>opts[:vsp_config].max_traffic_jitter,
- "network" => {"href" => opts[:network].id},
- "operations" => [{"rel"=>"edit", "href"=> id},
- {"rel"=>"delete", "href"=> id}]
- }
- vsp = CIMI::Model::VSP.from_json(JSON.generate(vsp_hash))
- @client.store_cimi(:vsp, vsp)
- vsp
- end
-
- def start_vsp(credentials, id)
- check_credentials(credentials)
- update_object_state(id, "VSP", "STARTED")
- end
-
- def stop_vsp(credentials, id)
- check_credentials(credentials)
- update_object_state(id, "VSP", "STOPPED")
- end
-
- def delete_vsp(credentials, id)
- check_credentials(credentials)
- @client.destroy_cimi(:vsp, id)
- end
-
def vsp_configurations(credentials, opts={})
check_credentials(credentials)
if opts[:id].nil?
@@ -189,56 +109,6 @@ module Deltacloud::Drivers::Mock
end
end
- def addresses(credentials, opts={})
- check_credentials(credentials)
- if opts[:id].nil?
- addresses = @client.load_all_cimi(:address).map{|addr| CIMI::Model::Address.from_json(addr)}
- addresses.map{|addr|convert_cimi_mock_urls(:address, addr, opts[:env])}.flatten
- else
- address = CIMI::Model::Address.from_json(@client.load_cimi(:address, opts[:id]))
- convert_cimi_mock_urls(:address, address, opts[:env])
- end
- end
-
- def create_address(credentials, opts={})
- check_credentials(credentials)
- id = "#{opts[:env].send("addresses_url")}/#{opts[:name]}"
- addr_hash = { "id" => id,
- "name" => opts[:name],
- "description" => opts[:description],
- "created" => Time.now,
- "hostName" => opts[:address_template].hostname,
- "allocation" => opts[:address_template].allocation,
- "defaultGateway" => opts[:address_template].default_gateway,
- "dns" => opts[:address_template].dns,
- "macAddress" => opts[:address_template].mac_address,
- "protocol" => opts[:address_template].protocol,
- "mask" => opts[:address_template].mask,
- "network" => {"href" => opts[:address_template].network.href},
- "operations" => [{"rel"=>"edit", "href"=> id},
- {"rel"=>"delete", "href"=> id}]
- }
- address = CIMI::Model::Address.from_json(JSON.generate(addr_hash))
- @client.store_cimi(:address, address)
- address
- end
-
- def delete_address(credentials, id)
- check_credentials(credentials)
- @client.destroy_cimi(:address, id)
- end
-
- def address_templates(credentials, opts={})
- check_credentials(credentials)
- if opts[:id].nil?
- address_templates = @client.load_all_cimi(:address_template).map{|addr_templ| CIMI::Model::AddressTemplate.from_json(addr_templ)}
- address_templates.map{|addr_templ|convert_cimi_mock_urls(:address_template, addr_templ, opts[:env])}.flatten
- else
- address_template = CIMI::Model::AddressTemplate.from_json(@client.load_cimi(:address_template, opts[:id]))
- convert_cimi_mock_urls(:address_template, address_template, opts[:env])
- end
- end
-
private
def convert_cimi_mock_urls(model_name, cimi_object, context)
@@ -270,15 +140,6 @@ module Deltacloud::Drivers::Mock
end
end
- def update_object_state(id, object, new_state)
- klass = CIMI::Model.const_get("#{object}")
- symbol = object.to_s.downcase.singularize.intern
- obj = klass.from_json(@client.load_cimi(symbol, id))
- obj.state = new_state
- @client.store_cimi(symbol, obj)
- obj
- end
-
end
end
diff --git a/server/lib/deltacloud/drivers/opennebula/opennebula_driver.rb b/server/lib/deltacloud/drivers/opennebula/opennebula_driver.rb
index 371e170..034b3bd 100644
--- a/server/lib/deltacloud/drivers/opennebula/opennebula_driver.rb
+++ b/server/lib/deltacloud/drivers/opennebula/opennebula_driver.rb
@@ -28,10 +28,6 @@ module Deltacloud
class OpennebulaDriver < Deltacloud::BaseDriver
- def supported_collections
- DEFAULT_COLLECTIONS - [:storage_volumes, :storage_snapshots]
- end
-
######################################################################
# Hardware profiles
#####################################################################
diff --git a/server/lib/deltacloud/drivers/openstack/openstack_driver.rb b/server/lib/deltacloud/drivers/openstack/openstack_driver.rb
index c9ec95b..12dadef 100644
--- a/server/lib/deltacloud/drivers/openstack/openstack_driver.rb
+++ b/server/lib/deltacloud/drivers/openstack/openstack_driver.rb
@@ -14,9 +14,9 @@
# under the License.
#
-require 'deltacloud/base_driver'
require 'openstack/compute'
require 'tempfile'
+
module Deltacloud
module Drivers
module Openstack
@@ -27,10 +27,6 @@ module Deltacloud
feature :instances, :user_files
feature :images, :user_name
- def supported_collections
- DEFAULT_COLLECTIONS - [ :storage_snapshots, :storage_volumes ] #+ [ :buckets ]
- end
-
define_instance_states do
start.to( :pending ) .on( :create )
pending.to( :running ) .automatically
diff --git a/server/lib/deltacloud/drivers/rackspace/rackspace_driver.rb b/server/lib/deltacloud/drivers/rackspace/rackspace_driver.rb
index 1b019d4..f59f832 100644
--- a/server/lib/deltacloud/drivers/rackspace/rackspace_driver.rb
+++ b/server/lib/deltacloud/drivers/rackspace/rackspace_driver.rb
@@ -14,7 +14,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-require 'deltacloud/base_driver'
require 'cloudfiles'
require 'cloudservers'
require 'base64'
@@ -30,10 +29,6 @@ class RackspaceDriver < Deltacloud::BaseDriver
feature :instances, :user_files
feature :images, :user_name
- def supported_collections
- DEFAULT_COLLECTIONS + [ :buckets ] - [ :storage_snapshots, :storage_volumes ]
- end
-
def hardware_profiles(credentials, opts = {})
rs = new_client( credentials )
results = []
@@ -194,8 +189,8 @@ class RackspaceDriver < Deltacloud::BaseDriver
start.to( :pending ) .on( :create )
pending.to( :running ) .automatically
running.to( :running ) .on( :reboot )
- running.to( :stopping ) .on( :stop )
- stopping.to( :stopped ) .automatically
+ running.to( :shutting_down ) .on( :stop )
+ shutting_down.to( :stopped ) .automatically
stopped.to( :finish ) .automatically
end
diff --git a/server/lib/deltacloud/drivers/rhevm/rhevm_driver.rb b/server/lib/deltacloud/drivers/rhevm/rhevm_driver.rb
index 1c96df7..f138f03 100644
--- a/server/lib/deltacloud/drivers/rhevm/rhevm_driver.rb
+++ b/server/lib/deltacloud/drivers/rhevm/rhevm_driver.rb
@@ -14,27 +14,24 @@
# License for the specific language governing permissions and limitations
# under the License.
-require 'deltacloud/base_driver'
require 'rbovirt'
module Deltacloud
module Drivers
- module RHEVM
+ module Rhevm
-class RHEVMDriver < Deltacloud::BaseDriver
+class RhevmDriver < Deltacloud::BaseDriver
- def supported_collections
- DEFAULT_COLLECTIONS - [:storage_snapshots]
- end
-
- feature :instances, :user_name do
- constraint :max_length, 50
+ Sinatra::Rabbit::InstancesCollection.features do
+ feature :user_name, :for => :instances do
+ constrain :max_length, 50
+ end
end
feature :instances, :user_data
feature :images, :user_name
- USER_NAME_MAX = feature(:instances, :user_name).constraints[:max_length]
+ USER_NAME_MAX = Sinatra::Rabbit::InstancesCollection.feature(:user_name).constraints[:max_length]
# FIXME: These values are just for ilustration
# Also I choosed 'SERVER' and 'DESKTOP' names
diff --git a/server/lib/deltacloud/drivers/rimuhosting/rimuhosting_client.rb b/server/lib/deltacloud/drivers/rimuhosting/rimuhosting_client.rb
index 58f8e1b..f44336a 100644
--- a/server/lib/deltacloud/drivers/rimuhosting/rimuhosting_client.rb
+++ b/server/lib/deltacloud/drivers/rimuhosting/rimuhosting_client.rb
@@ -41,17 +41,16 @@ class RimuHostingClient
headers["Authorization"] = @auth
end
safely do
- r = @service.send_request(method, @uri.path + resource, data, headers)
- puts r.body
- res = JSON.parse(r.body)
- res = res[res.keys[0]]
+ r = @service.send_request(method, @uri.path + resource, data, headers)
+ puts r.body
+ res = JSON.parse(r.body)
+ res = res[res.keys[0]]
- if(res['response_type'] == "ERROR" and ( (res['error_info']['error_class'] == "PermissionException") or
+ if(res['response_type'] == "ERROR" and ( (res['error_info']['error_class'] == "PermissionException") or
(res['error_info']['error_class'] == "LoginRequired") ))
- raise "AuthFailure"
- end
- res
+ raise "AuthFailure"
end
+ res
end
def list_images
@@ -82,6 +81,6 @@ class RimuHostingClient
end
end
+ end
end
end
-end
diff --git a/server/lib/deltacloud/drivers/rimuhosting/rimuhosting_driver.rb b/server/lib/deltacloud/drivers/rimuhosting/rimuhosting_driver.rb
index 67c415c..2a074d0 100644
--- a/server/lib/deltacloud/drivers/rimuhosting/rimuhosting_driver.rb
+++ b/server/lib/deltacloud/drivers/rimuhosting/rimuhosting_driver.rb
@@ -16,7 +16,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-require "deltacloud/base_driver"
require "deltacloud/drivers/rimuhosting/rimuhosting_client"
module Deltacloud
@@ -131,9 +130,9 @@ class RimuHostingDriver < Deltacloud::BaseDriver
:owner_id => "root",
:instance_profile => InstanceProfile.new("none"),
:actions => instance_actions_for("RUNNING"),
- :public_addresses => [ InstanceAddress.new(inst["allocated_ips"]["primary_ip"] ) ],
- :launch_time => inst["billing_info"]["order_date"]["iso_format"]
- })
+ :public_addresses => [ InstanceAddress.new(inst["allocated_ips"]["primary_ip"] )],
+ :launch_time => inst["billing_info"]["order_date"]["iso_format"]}
+ )
end
define_instance_states do
diff --git a/server/lib/deltacloud/drivers/terremark/terremark_driver.rb b/server/lib/deltacloud/drivers/terremark/terremark_driver.rb
index 2dba02a..7d7f482 100644
--- a/server/lib/deltacloud/drivers/terremark/terremark_driver.rb
+++ b/server/lib/deltacloud/drivers/terremark/terremark_driver.rb
@@ -19,8 +19,7 @@
# https://community.vcloudexpress.terremark.com/en-us/product_docs/w/wiki/d-complete-vcloud-express-api-document.aspx
#
# 02 May 2010
-#
-require 'deltacloud/base_driver'
+
require 'fog'
require 'excon'
require 'nokogiri'
diff --git a/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb b/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb
index e16be2f..a6a1e58 100644
--- a/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb
+++ b/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb
@@ -14,18 +14,17 @@
# under the License.
#
-require 'deltacloud/base_driver'
require 'rbvmomi'
require 'deltacloud/drivers/vsphere/vsphere_client'
-module Deltacloud::Drivers::VSphere
+module Deltacloud::Drivers::Vsphere
MAPPER_STORAGE_ROOT = File::join("/var/tmp", "deltacloud-vsphere-#{ENV["USER"]}")
- class VSphereDriver < Deltacloud::BaseDriver
+ class VsphereDriver < Deltacloud::BaseDriver
include Deltacloud::Drivers::VSphere::Helper
- include Deltacloud::Drivers::VSphere::FileManager
+ include VSphere::FileManager
# You can use 'user_iso' feature to set 'user_iso' parameter when creating
# a new instance where this parameter can hold gzipped CDROM iso which will
@@ -34,10 +33,6 @@ module Deltacloud::Drivers::VSphere
feature :instances, :user_data
feature :instances, :user_name
- def supported_collections
- DEFAULT_COLLECTIONS - [:storage_volumes, :storage_snapshots]
- end
-
# There is just one hardware profile where memory is measured using maximum
# memory available on ESX for virtual machines and CPU using maximum free
# CPU cores in ESX.
diff --git a/server/lib/deltacloud/models/state_machine.rb b/server/lib/deltacloud/models/state_machine.rb
index 19fb9f2..cbdaf4a 100644
--- a/server/lib/deltacloud/models/state_machine.rb
+++ b/server/lib/deltacloud/models/state_machine.rb
@@ -18,7 +18,10 @@ module Deltacloud
class StateMachine
attr_reader :states
- def initialize(&block)
+
+ def initialize(opts={}, &block)
+ @all_states = opts[:all_states]
+ @all_actions = opts[:all_actions]
@states = []
instance_eval &block if block
end
@@ -32,6 +35,9 @@ module Deltacloud
end
def state(name)
+ unless valid_state_name?(name)
+ raise "State '#{name}' not in list of allowed states"
+ end
state = @states.find{|e| e.name == name.to_sym}
if ( state.nil? )
state = State.new( self, name.to_sym )
@@ -40,6 +46,14 @@ module Deltacloud
state
end
+ def valid_state_name?(name)
+ @all_states.nil? || @all_states.include?(name.to_sym)
+ end
+
+ def valid_action_name?(name)
+ @all_actions.nil? || @all_actions.include?(name.to_sym)
+ end
+
def method_missing(sym,*args)
return state( sym ) if ( args.empty? )
super( sym, *args )
@@ -90,6 +104,9 @@ module Deltacloud
end
def on(action)
+ unless @machine.valid_action_name?(action)
+ raise "Action '#{action}' not in list of allowed actions"
+ end
@action = action
end
--
1.7.10.1
[PATCH core 05/26] Core: Added Deltacloud::API class and helpers to serve the default entrypoint
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/lib/deltacloud/helpers.rb | 2 +-
server/lib/deltacloud/server.rb | 1293 +-------------------------------------
2 files changed, 29 insertions(+), 1266 deletions(-)
diff --git a/server/lib/deltacloud/helpers.rb b/server/lib/deltacloud/helpers.rb
index dc382bd..8e265fc 100644
--- a/server/lib/deltacloud/helpers.rb
+++ b/server/lib/deltacloud/helpers.rb
@@ -1,4 +1,3 @@
-#
# 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
@@ -91,5 +90,6 @@ module Deltacloud::Collections
def self.route_for(url)
"#{settings.root_url}#{url}"
end
+
end
end
diff --git a/server/lib/deltacloud/server.rb b/server/lib/deltacloud/server.rb
index 46b4d25..ebb7cb7 100644
--- a/server/lib/deltacloud/server.rb
+++ b/server/lib/deltacloud/server.rb
@@ -1,1281 +1,44 @@
-# 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.
-
-require 'sinatra'
-require 'deltacloud'
+require 'rubygems'
+require 'crack'
require 'json'
-require 'sinatra/rack_accept'
-require 'sinatra/static_assets'
-require 'sinatra/rabbit'
-require 'sinatra/lazy_auth'
-require 'erb'
+require 'yaml'
require 'haml'
-require 'open3'
-require 'sinatra/sinatra_verbose'
-require 'sinatra/rack_driver_select'
-require 'sinatra/rack_runtime'
-require 'sinatra/rack_etag'
-require 'sinatra/rack_date'
-require 'sinatra/rack_matrix_params'
-require 'sinatra/rack_syslog'
-
-set :version, '0.5.0'
-
-include Deltacloud::Drivers
-set :drivers, Proc.new { driver_config }
-
-Sinatra::Application.register Rack::RespondTo
-
-use Rack::ETag
-use Rack::Runtime
-use Rack::MatrixParams
-use Rack::DriverSelect
-use Rack::MediaType
-use Rack::Date
-use Rack::CommonLogger
-
-configure do
- set :root_url, "/api"
- set :views, File::join($top_srcdir, 'views')
- # NOTE: Change :public to :public_folder once we update sinatra to 1.3
- # set :public_folder, File::join($top_srcdir, 'public')
- if settings.respond_to? :public_folder
- set :public_folder, File::join($top_srcdir, 'public')
- else
- set :public, File::join($top_srcdir, 'public')
- end
- # Try to load the driver on startup to fail early if there are issues
- driver
-end
-
-configure :production do
- use Rack::SyslogLogger
- set :logger, SyslogFile.new
- disable :logging
- enable :show_errors
- enable :dump_errors
-end
-
-configure :development do
- # So we can just use puts for logging
- set :raise_errors => false
- set :show_exceptions, false
- $stdout.sync = true
- $stderr.sync = true
-end
-
-# You could use $API_HOST environment variable to change your hostname to
-# whatever you want (eg. if you running API behind NAT)
-HOSTNAME=ENV['API_HOST'] ? ENV['API_HOST'] : nil
-
-error do
- report_error
-end
-
-error Deltacloud::ExceptionHandler::ValidationFailure do
- report_error
-end
-
-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
-end
-
-after do
- headers 'Server' => 'Apache-Deltacloud/' + settings.version
-end
-
-# Redirect to /api
-get '/' do redirect settings.root_url, 301; end
-
-
-# Generate a root route for API docs
-get "#{settings.root_url}/docs\/?" do
- respond_to do |format|
- format.html { haml :'docs/index' }
- format.xml { haml :'docs/index' }
- end
-end
-
-get "#{settings.root_url}\/?" do
- if params[:force_auth]
- return [401, 'Authentication failed'] unless driver.valid_credentials?(credentials)
- end
- @collections = [:drivers] + driver.supported_collections
- @driver_name = driver.name unless driver.name.to_sym == DRIVER
- @providers = driver.configured_providers
- respond_to do |format|
- format.xml { haml :"api/show" }
- format.json do
- { :api => {
- :version => settings.version,
- :driver => driver_symbol,
- :links => entry_points.collect do |l|
- { :rel => l[0], :href => l[1] }.merge(json_features_for_entrypoint(l))
- end
- }
- }.to_json
- end
- format.html { haml :"api/show" }
- end
-end
-
-post "#{settings.root_url}\/?" do
- p = {}
- ["provider", "driver"].each { |k| p[k] = params[k] if params[k] }
- p.delete("provider") if p["provider"] == "default"
- q = p.map { |k,v| "#{k}=#{v}" }.join(";")
- q = ";" + q unless q.empty?
- redirect "#{settings.root_url}#{q}", 301
-end
-
-# Rabbit DSL
-
-collection :drivers do
- global!
-
- description <<EOS
-List all the drivers supported by this server.
-EOS
-
- operation :index do
- description "List all drivers"
- control do
- @drivers = settings.drivers
- respond_to do |format|
- format.xml { haml :"drivers/index" }
- format.json { @drivers.to_json }
- format.html { haml :"drivers/index" }
- end
- end
- end
-
- operation :show do
- description "Show details for a driver"
- param :id, :string
- control do
- @name = params[:id].to_sym
- if driver_symbol == @name
- @providers = driver.providers(credentials) if driver.respond_to? :providers
- end
- @driver = settings.drivers[@name]
- halt 404 unless @driver
- respond_to do |format|
- format.xml { haml :"drivers/show" }
- format.json { @driver.to_json }
- format.html { haml :"drivers/show" }
- end
- end
- end
-end
-
-collection :realms do
- description <<END
- Within a cloud provider a realm represents a boundary containing resources.
- The exact definition of a realm is left to the cloud provider.
- In some cases, a realm may represent different datacenters, different continents,
- or different pools of resources within a single datacenter.
- A cloud provider may insist that resources must all exist within a single realm in
- order to cooperate. For instance, storage volumes may only be allowed to be mounted to
- instances within the same realm.
-END
-
- operation :index do
- description <<END
- Operation will list all available realms. Realms can be filtered using
- the "architecture" parameter.
-END
- with_capability :realms
- param :id, :string
- param :architecture, :string, :optional, [ 'i386', 'x86_64' ]
- control { filter_all(:realms) }
- end
-
- #FIXME: It always shows whole list
- operation :show do
- description 'Show an realm identified by "id" parameter.'
- with_capability :realm
- param :id, :string, :required
- control { show(:realm) }
- end
-
-end
-
-collection :images do
- description <<END
- An image is a platonic form of a machine. Images are not directly executable,
- but are a template for creating actual instances of machines.
-END
-
- operation :new do
- description "Form to create a new image resource"
- param :instance_id, :string, "An instance from which the new image will be created from"
- control do
- @instance = Instance.new( :id => params[:instance_id] )
- respond_to do |format|
- format.html { haml :"images/new" }
- end
- end
- end
-
- operation :index do
- description <<END
- The images collection will return a set of all images
- available to the current use. Images can be filtered using the
- "owner_id" and "architecture" parameters.
-END
- with_capability :images
- param :id, :string
- param :architecture, :string, :optional
- control { filter_all(:images) }
- end
-
- operation :show do
- description 'Show an image identified by "id" parameter.'
- with_capability :image
- param :id, :string, :required
- control { show(:image) }
- end
-
- operation :create do
- description 'Create image from instance'
- with_capability :create_image
- param :instance_id, :string, :required
- control do
- @image = driver.create_image(credentials, {
- :id => params[:instance_id],
- :name => params[:name],
- :description => params[:description]
- })
- status 201 # Created
- response['Location'] = image_url(@image.id)
- respond_to do |format|
- format.xml { haml :"images/show" }
- format.json { convert_to_json(:image, @image) }
- format.html { haml :"images/show" }
- end
- end
- end
-
- operation :destroy do
- description "Remove specified image from collection"
- with_capability :destroy_image
- param :id, :string, :required
- control do
- driver.destroy_image(credentials, params[:id])
- status 204
- respond_to do |format|
- format.xml
- format.json
- format.html { redirect(images_url) }
- end
- end
- end
-
-end
-
-collection :instance_states do
- description "The possible states of an instance, and how to traverse between them "
-
- operation :index do
- control do
- @machine = driver.instance_state_machine
- respond_to do |format|
- format.xml { haml :'instance_states/show', :layout => false }
- format.json do
- out = []
- @machine.states.each do |state|
- transitions = state.transitions.collect do |t|
- t.automatically? ? {:to => t.destination, :auto => 'true'} : {:to => t.destination, :action => t.action}
- end
- out << { :name => state, :transitions => transitions }
- end
- out.to_json
- end
- format.html { haml :'instance_states/show'}
- format.gv { erb :"instance_states/show" }
- format.png do
- # Trick respond_to into looking up the right template for the
- # graphviz file
- gv = erb(:"instance_states/show")
- png = ''
- cmd = 'dot -Kdot -Gpad="0.2,0.2" -Gsize="5.0,8.0" -Gdpi="180" -Tpng'
- Open3.popen3( cmd ) do |stdin, stdout, stderr|
- stdin.write( gv )
- stdin.close()
- png = stdout.read
- end
- content_type 'image/png'
- png
- end
- end
- end
- end
-end
-
-get "#{settings.root_url}/instances/:id/run" do
- @instance = driver.instance(credentials, :id => params[:id])
- respond_to do |format|
- format.html { haml :"instances/run_command" }
- end
-end
-
-collection :load_balancers do
- description "Load balancers"
-
- operation :new do
- description "Form to create a new load balancer"
- control do
- @realms = driver.realms(credentials)
- @instances = driver.instances(credentials) if driver_has_feature?(:register_instance, :load_balancers)
- respond_to do |format|
- format.html { haml :"load_balancers/new" }
- end
- end
- end
-
- operation :index do
- description "List of all active load balancers"
- control do
- filter_all :load_balancers
- end
- end
-
- operation :show do
- description "Show details about given load balancer"
- param :id, :string, :required
- control { show :load_balancer }
- end
-
- operation :create do
- description "Create a new load balancer"
- param :name, :string, :required
- param :realm_id, :string, :required
- param :listener_protocol, :string, :required, ['HTTP', 'TCP']
- param :listener_balancer_port, :string, :required
- param :listener_instance_port, :string, :required
- control do
- @load_balancer = driver.create_load_balancer(credentials, params)
- status 201 # Created
- response['Location'] = load_balancer_url(@instance.id)
- respond_to do |format|
- format.xml { haml :"load_balancers/show" }
- format.json { convert_to_json(:load_balancer, @load_balancer) }
- format.html { haml :"load_balancers/show" }
- end
- end
- end
-
- operation :register, :method => :post, :member => true do
- description "Add instance to loadbalancer"
- param :id, :string, :required
- param :instance_id, :string, :required
- control do
- driver.lb_register_instance(credentials, params)
- status 204
- respond_to do |format|
- format.xml
- format.json
- format.html { redirect(load_balancer_url(params[:id])) }
- end
- end
- end
-
- operation :unregister, :method => :post, :member => true do
- description "Remove instance from loadbalancer"
- param :id, :string, :required
- param :instance_id, :string, :required
- control do
- driver.lb_unregister_instance(credentials, params)
- status 204
- respond_to do |format|
- format.xml
- format.json
- format.html { redirect(load_balancer_url(params[:id])) }
- end
- end
- end
-
- operation :destroy do
- description "Destroy given load balancer"
- param :id, :string, :required
- control do
- driver.destroy_load_balancer(credentials, params[:id])
- status 204
- respond_to do |format|
- format.xml
- format.json
- format.html { redirect(load_balancers_url) }
- end
- end
- end
-
-end
-
-
-collection :instances do
- description <<END
- An instance is a concrete machine realized from an image.
- The images collection may be obtained by following the link from the primary entry-point.
-END
-
- operation :new do
- description "Form for creating a new instance resource"
- param :image_id, :string, "Image from which will be the new instance created from"
- param :realm_id, :string, :optional
- if driver_has_feature? :authentication_key
- param :authentication_key, :string, :optional
- end
- if driver_has_feature? :firewalls
- param :firewalls, :string, :optional
- end
- control do
- @instance = Instance.new( { :id=>params[:id], :image_id=>params[:image_id] } )
- @image = Image.new( :id => params[:image_id] )
- @hardware_profiles = driver.hardware_profiles(credentials, :architecture => @image.architecture )
- @realms = [Realm.new(:id => params[:realm_id])] if params[:realm_id]
- @realms ||= driver.realms(credentials)
- @keys = driver.keys(credentials) if driver_has_feature?(:authentication_key)
- @firewalls = driver.firewalls(credentials) if driver_has_feature?(:firewalls)
- respond_to do |format|
- format.html do
- haml :'instances/new'
- end
- end
- end
- end
-
- operation :index do
- description "List all instances."
- with_capability :instances
- param :id, :string, :optional
- param :state, :string, :optional
- control { filter_all(:instances) }
- end
-
- operation :show do
- description 'Show an instance identified by "id" parameter.'
- with_capability :instance
- param :id, :string, :required
- control { show(:instance) }
- end
-
- operation :create do
- description "Create a new instance."
- with_capability :create_instance
- param :image_id, :string, :required
- param :realm_id, :string, :optional
- param :hwp_id, :string, :optional
- control do
- @instance = driver.create_instance(credentials, params[:image_id], params)
- if @instance.kind_of? Array
- @elements = @instance
- action_handler = "index"
- else
- response['Location'] = instance_url(@instance.id)
- action_handler = "show"
- end
- status 201 # Created
- respond_to do |format|
- format.xml { haml :"instances/#{action_handler}" }
- format.json do
- if @elements
- convert_to_json(:instances, @elements)
- else
- convert_to_json(:instance, @instance)
- end
- end
- format.html do
- if @elements
- haml :"instances/index"
- elsif @instance and @instance.id
- response['Location'] = instance_url(@instance.id)
- haml :"instances/show"
- else
- redirect instances_url
- end
- end
- end
- end
- end
-
- operation :reboot, :method => :post, :member => true do
- description "Reboot a running instance."
- with_capability :reboot_instance
- param :id, :string, :required
- control { instance_action(:reboot) }
- end
-
- operation :start, :method => :post, :member => true do
- description "Start an instance."
- with_capability :start_instance
- param :id, :string, :required
- control { instance_action(:start) }
- end
-
- operation :stop, :method => :post, :member => true do
- description "Stop a running instance."
- with_capability :stop_instance
- param :id, :string, :required
- control { instance_action(:stop) }
- end
-
- operation :destroy do
- description "Destroy an instance."
- with_capability :destroy_instance
- param :id, :string, :required
- control { instance_action(:destroy) }
- end
-
- operation :run, :method => :post, :member => true do
- description <<END
- Run command on instance. Either password or private key must be send
- in order to execute command. Authetication method should be advertised
- in instance.
-END
- with_capability :run_on_instance
- param :id, :string, :required
- param :cmd, :string, :required, [], "Shell command to run on instance"
- param :private_key, :string, :optional, [], "Private key in PEM format for authentication"
- param :password, :string, :optional, [], "Password used for authentication"
- control do
- @output = driver.run_on_instance(credentials, params)
- respond_to do |format|
- format.xml { haml :"instances/run" }
- format.html { haml :"instances/run" }
- end
- end
- end
-end
-
-collection :metrics do
-
- operation :index do
- with_capability :metrics
- control { filter_all(:metrics) }
- end
-
- operation :show do
- with_capability :metrics
- param :id, :string, :required
- control { show :metric }
- end
-
-end
-
-collection :hardware_profiles do
- description <<END
- A hardware profile represents a configuration of resources upon which a
- machine may be deployed. It defines aspects such as local disk storage,
- available RAM, and architecture. Each provider is free to define as many
- (or as few) hardware profiles as desired.
-END
-
- operation :index do
- description "List of available hardware profiles."
- with_capability :hardware_profiles
- param :id, :string
- param :architecture, :string, :optional, [ 'i386', 'x86_64' ]
- control do
- @profiles = driver.hardware_profiles(credentials, params)
- respond_to do |format|
- format.xml { haml :'hardware_profiles/index' }
- format.html { haml :'hardware_profiles/index' }
- format.json { convert_to_json(:hardware_profile, @profiles) }
- end
- end
- end
-
- operation :show do
- description "Show specific hardware profile."
- with_capability :hardware_profile
- param :id, :string, :required
- control do
- @profile = driver.hardware_profile(credentials, params[:id])
- if @profile
- respond_to do |format|
- format.xml { haml :'hardware_profiles/show', :layout => false }
- format.html { haml :'hardware_profiles/show' }
- format.json { convert_to_json(:hardware_profile, @profile) }
- end
- else
- report_error(404)
- end
- end
- end
-
-end
-
-collection :storage_snapshots do
- description "Storage snapshots description here"
-
- operation :new do
- description "A form to create a new storage snapshot"
- control do
- respond_to do |format|
- format.html { haml :"storage_snapshots/new" }
- end
- end
- end
-
- operation :index do
- description "List of storage snapshots."
- with_capability :storage_snapshots
- param :id, :string
- control { filter_all(:storage_snapshots) }
- end
-
- operation :show do
- description "Show storage snapshot."
- with_capability :storage_snapshot
- param :id, :string, :required
- control { show(:storage_snapshot) }
- end
-
- operation :create do
- description "Create a new snapshot from volume"
- with_capability :create_storage_snapshot
- param :volume_id, :string, :required
- control do
- @storage_snapshot = driver.create_storage_snapshot(credentials, params)
- status 201 # Created
- response['Location'] = storage_snapshot_url(@storage_snapshot.id)
- show(:storage_snapshot)
- end
- end
-
- operation :destroy do
- description "Delete storage snapshot"
- with_capability :destroy_storage_snapshot
- param :id, :string, :required
- control do
- driver.destroy_storage_snapshot(credentials, params)
- status 204
- respond_to do |format|
- format.xml
- format.json
- format.html { redirect(storage_snapshots_url) }
- end
- end
- end
-end
-
-collection :storage_volumes do
- description "Storage volumes description here"
-
- operation :new do
- description "A form to create a new storage volume"
- control do
- respond_to do |format|
- format.html { haml :"storage_volumes/new" }
- end
- end
- end
-
- operation :index do
- description "List of storage volumes."
- with_capability :storage_volumes
- param :id, :string
- control { filter_all(:storage_volumes) }
- end
-
- operation :show do
- description "Show storage volume."
- with_capability :storage_volume
- param :id, :string, :required
- control { show(:storage_volume) }
- end
-
- operation :create do
- description "Create a new storage volume"
- with_capability :create_storage_volume
- param :snapshot_id, :string, :optional
- param :capacity, :string, :optional
- param :realm_id, :string, :optional
- control do
- @storage_volume = driver.create_storage_volume(credentials, params)
- status 201
- response['Location'] = storage_volume_url(@storage_volume.id)
- respond_to do |format|
- format.xml { haml :"storage_volumes/show" }
- format.html { haml :"storage_volumes/show" }
- format.json { convert_to_json(:storage_volume, @storage_volume) }
- end
- end
- end
-
- operation :attach_instance, :method=>:get, :member=>true do
- description "A form to attach a storage volume to an instance"
- control do
- @instances = driver.instances(credentials)
- respond_to do |format|
- format.html{ haml :"storage_volumes/attach"}
- end
- end
- end
-
- operation :attach, :method => :post, :member => true do
- description "Attach storage volume to instance"
- with_capability :attach_storage_volume
- param :id, :string, :required
- param :instance_id,:string, :required
- param :device, :string, :required
- control do
- @storage_volume = driver.attach_storage_volume(credentials, params)
- status 202
- respond_to do |format|
- format.html { redirect(storage_volume_url(params[:id]))}
- format.xml { haml :"storage_volumes/show" }
- format.json { convert_to_json(:storage_volume, @storage_volume) }
- end
- end
- end
-
- operation :detach, :method => :post, :member => true do
- description "Detach storage volume to instance"
- with_capability :detach_storage_volume
- param :id, :string, :required
- control do
- volume = driver.storage_volume(credentials, :id => params[:id])
- @storage_volume = driver.detach_storage_volume(credentials, :id => volume.id, :instance_id => volume.instance_id, :device => volume.device)
- status 202
- respond_to do |format|
- format.html { redirect(storage_volume_url(params[:id]))}
- format.xml { haml :"storage_volumes/show" }
- format.json { convert_to_json(:storage_volume, @storage_volume) }
- end
- end
- end
-
- operation :destroy do
- description "Destroy storage volume"
- with_capability :destroy_storage_volume
- param :id, :string, :optional
- control do
- driver.destroy_storage_volume(credentials, params)
- status 204
- respond_to do |format|
- format.xml
- format.json
- format.html { redirect(storage_volumes_url) }
- end
- end
- end
-
-end
-
-collection :keys do
- description "Instance authentication credentials."
-
- operation :new do
- description "A form to create a new key resource"
- control do
- respond_to do |format|
- format.html { haml :"keys/new" }
- end
- end
- end
-
- operation :index do
- description "List all available credentials which could be used for instance authentication."
- with_capability :keys
- control do
- filter_all :keys
- end
- end
-
- operation :show do
- description "Show details about given instance credential."
- with_capability :key
- param :id, :string, :required
- control { show :key }
- end
-
- operation :create do
- description "Create a new instance credential if backend supports this."
- with_capability :create_key
- param :name, :string, :required
- control do
- @key = driver.create_key(credentials, { :key_name => params[:name] })
- status 201
- response['Location'] = key_url(@key.id)
- respond_to do |format|
- format.xml { haml :"keys/show", :ugly => true }
- format.html { haml :"keys/show" }
- format.json { convert_to_json(:key, @key)}
- end
- end
- end
-
- operation :destroy do
- description "Destroy given instance credential if backend supports this."
- with_capability :destroy_key
- param :id, :string, :required
- control do
- driver.destroy_key(credentials, { :id => params[:id]})
- status 204
- respond_to do |format|
- format.xml
- format.json
- format.html { redirect(keys_url) }
- end
- end
- end
-
-end
-
-#get html form for creating a new blob
-
-# The URL for getting the new blob form for the HTML UI looks like the URL
-# for getting the details of an existing blob. To make collisions less
-# likely, we use a name for the form that will rarely be the name of an
-# existing blob
-NEW_BLOB_FORM_ID = "new_blob_form_d15cfd90"
-
-get "#{settings.root_url}/buckets/:bucket/#{NEW_BLOB_FORM_ID}" do
- @bucket_id = params[:bucket]
- respond_to do |format|
- format.html {haml :"blobs/new"}
- end
-end
-
-collection :buckets do
- description "Cloud Storage buckets - aka buckets|directories|folders"
-
- collection :blobs do
- description "Blobs associated with given bucket"
-
- operation :show do
- description "Display blob"
- control do
- @blob = driver.blob(credentials, { :id => params[:blob], 'bucket' => params[:bucket]})
- if @blob
- respond_to do |format|
- format.xml { haml :"blobs/show" }
- format.html { haml :"blobs/show" }
- format.json { convert_to_json(:blob, @blob) }
- end
- else
- report_error(404)
- end
- end
-
- end
-
- operation :create do
- description "Create new blob"
- param :blob_id, :string, :required
- param :blob_data, :hash, :required
- control do
- bucket_id = params[:bucket]
- blob_id = params['blob_id']
- blob_data = params['blob_data']
- user_meta = {}
- #metadata from params (i.e., passed by http form post, e.g. browser)
- max = params[:meta_params]
- if(max)
- (1..max.to_i).each do |i|
- key = params[:"meta_name#{i}"]
- key = "HTTP_X_Deltacloud_Blobmeta_#{key}"
- value = params[:"meta_value#{i}"]
- user_meta[key] = value
- end
- end
- @blob = driver.create_blob(credentials, bucket_id, blob_id, blob_data, user_meta)
- respond_to do |format|
- format.xml { haml :"blobs/show" }
- format.html { haml :"blobs/show"}
- format.json {convert_to_json(:blob, @blob)}
- end
- end
- end
-
- operation :destroy do
- description "Destroy given blob"
- control do
- bucket_id = params[:bucket]
- blob_id = params[:blob]
- driver.delete_blob(credentials, bucket_id, blob_id)
- status 204
- respond_to do |format|
- format.xml
- format.json
- format.html { redirect(bucket_url(bucket_id)) }
- end
- end
- end
-
- operation :stream, :member => true, :standard => true, :method => :put do
- description "Stream new blob data into the blob"
- control do
- 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],
- 'bucket' => params[:bucket]})
- respond_to do |format|
- format.xml { haml :"blobs/show" }
- format.html { haml :"blobs/show" }
- format.json { convert_to_json(:blob, @blob) }
- end
- elsif(env["BLOB_FAIL"])
- report_error(500) #OK?
- else # small blobs - < 112kb dont hit the streaming monkey patch - use 'normal' create_blob
- # also, if running under webrick don't hit the streaming patch (Thin specific)
- bucket_id = params[:bucket]
- blob_id = params[:blob]
- temp_file = Tempfile.new("temp_blob_file")
- temp_file.write(env['rack.input'].read)
- temp_file.flush
- 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)
- temp_file.delete
- respond_to do |format|
- format.xml { haml :"blobs/show" }
- format.html { haml :"blobs/show" }
- format.json { convert_to_json(:blob, @blob) }
- end
- end
- end
- end
+require 'sinatra/base'
+require 'sinatra/rabbit'
- operation :metadata, :member => true, :standard => true, :method => :head do
- description "Get blob metadata"
- control do
- @blob_id = params[:blob]
- @blob_metadata = driver.blob_metadata(credentials, {:id => params[:blob], 'bucket' => params[:bucket]})
- if @blob_metadata
- @blob_metadata.each do |k,v|
- headers["X-Deltacloud-Blobmeta-#{k}"] = v
- end
- status 204
- respond_to do |format|
- format.xml
- format.json
- end
- else
- report_error(404)
- end
- end
- end
+require_relative '../sinatra'
+require_relative './models'
+require_relative './drivers'
+require_relative './helpers'
+require_relative './collections'
- operation :update, :member => true, :method => :post do
- description "Update blob metadata"
- control do
- meta_hash = BlobHelper::extract_blob_metadata_hash(request.env)
- success = driver.update_blob_metadata(credentials, {'bucket'=>params[:bucket], :id =>params[:blob], 'meta_hash' => meta_hash})
- if(success)
- meta_hash.each do |k,v|
- headers["X-Deltacloud-Blobmeta-#{k}"] = v
- end
- status 204
- respond_to do |format|
- format.xml
- format.json
- end
- else
- report_error(404) #FIXME is this the right error code?
- end
- end
- end
- operation :content, :member => true, :method => :get do
- description "Download blob content"
- control do
- @blob = driver.blob(credentials, { :id => params[:blob], 'bucket' => params[:bucket]})
- if @blob
- params['content_length'] = @blob.content_length
- params['content_type'] = @blob.content_type
- params['content_disposition'] = "attachment; filename=#{@blob.id}"
- BlobStream.call(env, credentials, params)
- else
- report_error(404)
- end
- end
- end
+module Deltacloud
+ class API < Collections::Base
- end
+ # Enable logging
+ use Rack::CommonLogger
+ use Rack::Date
+ use Rack::ETag
+ use Rack::MatrixParams
+ use Rack::DriverSelect
+ use Rack::Accept
+ use Rack::MediaType
- operation :new do
- description "A form to create a new bucket resource"
- control do
- respond_to do |format|
- format.html { haml :"buckets/new" }
- end
- end
- end
-
- operation :index do
- description "List buckets associated with this account"
- with_capability :buckets
- param :id, :string
- param :name, :string
- param :size, :string
- control { filter_all(:buckets) }
- end
-
- operation :show do
- description "Show bucket"
- with_capability :bucket
- param :id, :string
- control { show(:bucket) }
- end
+ include Deltacloud::Helpers
+ include Deltacloud::Collections
- operation :create do
- description "Create a new bucket (POST /api/buckets)"
- with_capability :create_bucket
- param :name, :string, :required
- control do
- @bucket = driver.create_bucket(credentials, params[:name], params)
- status 201
- response['Location'] = bucket_url(@bucket.id)
- respond_to do |format|
- format.xml { haml :"buckets/show" }
- format.json { convert_to_json(:bucket, @bucket) }
- format.html do
- redirect bucket_url(@bucket.id) if @bucket and @bucket.id
- redirect buckets_url
- end
+ get API_ROOT_URL do
+ if params[:force_auth]
+ return [401, 'Authentication failed'] unless driver.valid_credentials?(credentials)
end
- end
- end
-
- operation :destroy do
- description "Delete a bucket by name - bucket must be empty"
- with_capability :delete_bucket
- param :id, :string, :required
- control do
- driver.delete_bucket(credentials, params[:id], params)
- status 204
respond_to do |format|
- format.xml
- format.json
- format.html { redirect(buckets_url) }
+ format.xml { haml :"api/show" }
+ format.json { xml_to_json :"api/show" }
+ format.html { haml :"api/show" }
end
end
- end
-end
-
-get "#{settings.root_url}/addresses/:id/associate" do
- @instances = driver.instances(credentials)
- @address = Address::new(:id => params[:id])
- respond_to do |format|
- format.html { haml :"addresses/associate" }
end
end
-collection :addresses do
- description "Manage IP addresses"
-
- operation :index do
- description "List IP addresses assigned to your account."
- with_capability :addresses
- control do
- filter_all :addresses
- end
- end
-
- operation :show do
- description "Show details about IP addresses specified by given ID"
- with_capability :address
- param :id, :string, :required
- control { show :address }
- end
-
- operation :create do
- description "Acquire a new IP address for use with your account."
- with_capability :create_address
- control do
- @address = driver.create_address(credentials, {})
- status 201 # Created
- response['Location'] = address_url(@address.id)
- respond_to do |format|
- format.xml { haml :"addresses/show", :ugly => true }
- format.html { haml :"addresses/_address", :layout => false }
- format.json { convert_to_json(:address, @address) }
- end
- end
- end
-
- operation :destroy do
- description "Release an IP address associated with your account"
- with_capability :destroy_address
- param :id, :string, :required
- control do
- driver.destroy_address(credentials, { :id => params[:id]})
- status 204
- respond_to do |format|
- format.xml
- format.json
- format.html { redirect(addresses_url) }
- end
- end
- end
-
- operation :associate, :method => :post, :member => true do
- description "Associate an IP address to an instance"
- with_capability :associate_address
- param :id, :string, :required
- param :instance_id, :string, :required
- control do
- driver.associate_address(credentials, { :id => params[:id], :instance_id => params[:instance_id]})
- status 202 # Accepted
- respond_to do |format|
- format.xml
- format.json
- format.html { redirect(address_url(params[:id])) }
- end
- end
- end
-
- operation :disassociate, :method => :post, :member => true do
- description "Disassociate an IP address from an instance"
- with_capability :associate_address
- param :id, :string, :required
- control do
- driver.disassociate_address(credentials, { :id => params[:id] })
- status 202 # Accepted
- respond_to do |format|
- format.xml
- format.json
- format.html { redirect(address_url(params[:id])) }
- end
- end
- end
-
-end
-
-#delete a firewall rule
-delete '/api/firewalls/:firewall/:rule' do
- opts = {}
- opts[:firewall] = params[:firewall]
- opts[:rule_id] = params[:rule]
- driver.delete_firewall_rule(credentials, opts)
- status 204
- respond_to do |format|
- format.xml
- format.json
- format.html {redirect firewall_url(params[:firewall])}
- end
-end
-
-#FIREWALLS
-collection :firewalls do
- description "Allow user to define firewall rules for an instance (ec2 security groups) eg expose ssh access [port 22, tcp]."
-
- operation :new do
- description "A form to create a new firewall resource"
- control do
- respond_to do |format|
- format.html { haml :"firewalls/new" }
- end
- end
- end
-
- operation :new_rule, :form => true, :member => true, :method => :get do
- description "A form to create a new firewall rule"
- param :id, :string, :required
- control do
- @firewall_name = params[:id]
- respond_to do |format|
- format.html {haml :"firewalls/new_rule" }
- end
- end
- end
-
- operation :index do
- description 'List all firewalls'
- with_capability :firewalls
- control { filter_all(:firewalls) }
- end
-
- operation :show do
- description 'Show details for a specific firewall - list all rules'
- with_capability :firewall
- param :id, :string, :required
- control { show(:firewall) }
- end
-
- operation :create do
- description 'Create a new firewall'
- with_capability :create_firewall
- param :name, :string, :required
- param :description, :string, :required
- control do
- @firewall = driver.create_firewall(credentials, params )
- status 201 # Created
- response['Location'] = firewall_url(@firewall.id)
- respond_to do |format|
- format.xml { haml :"firewalls/show" }
- format.html { haml :"firewalls/show" }
- format.json { convert_to_json(:firewall, @firewall) }
- end
- end
- end
-
- operation :destroy do
- description 'Delete a specified firewall - error if firewall has rules'
- with_capability :delete_firewall
- param :id, :string, :required
- control do
- driver.delete_firewall(credentials, params)
- status 204
- respond_to do |format|
- format.xml
- format.json
- format.html { redirect(firewalls_url) }
- end
- end
- end
-
- #create a new firewall rule - POST /api/firewalls/:firewall/rules
- operation :rules, :method => :post, :member => true do
- description 'Create a new firewall rule for the specified firewall'
- param :id, :required, :string, [], "Name of firewall in which to apply this rule"
- param :protocol, :required, :string, ['tcp','udp','icmp'], "Transport layer protocol for the rule"
- param :port_from, :required, :string, [], "Start of port range for the rule"
- param :port_to, :required, :string, [], "End of port range for the rule"
- with_capability :create_firewall_rule
- control do
- #source IPs from params
- addresses = params.inject([]){|result,current| result << current.last unless current.grep(/^ip[-_]address/i).empty?; result}
- #source groups from params
- groups = {}
- max_groups = params.select{|k,v| k=~/^group/}.size/2
- for i in (1..max_groups) do
- groups.merge!({params["group#{i}"]=>params["group#{i}owner"]})
- end
- params['addresses'] = addresses
- params['groups'] = groups
- if addresses.empty? && groups.empty?
- raise Deltacloud::ExceptionHandler::ValidationFailure.new(
- StandardError.new("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]})
- status 201
- respond_to do |format|
- format.xml { haml :"firewalls/show" }
- format.html { haml :"firewalls/show" }
- format.json { convert_to_json(:firewall, @firewall) }
- end
- end
- end
-
-end
--
1.7.10.1
Re: [PATCH core 26/26] FGCP: Fixed configuration of providers
Posted by Michal Fojtik <mi...@mifo.sk>.
On 16 May 2012, at 4:02, Koper, Dies wrote:
Hi Dies,
Then I vote for updating the client and driver, because without this
patch the GET /api/drivers?format=xml returns:
NoMethodError - undefined method `each' for
"https://api.oviss.jp.fujitsu.com/ovissapi/endpoint":String:
/Users/mfojtik/code/deltacloud/server/lib/deltacloud/helpers/deltacloud_helper.rb:188:in
`block in driver_provider'
/Users/mfojtik/code/deltacloud/server/lib/deltacloud/helpers/deltacloud_helper.rb:187:in
`each'
/Users/mfojtik/code/deltacloud/server/lib/deltacloud/helpers/deltacloud_helper.rb:187:in
`driver_provider'
/Users/mfojtik/code/deltacloud/server/lib/deltacloud/../../views/drivers/index.xml.haml:6:in
`block in evaluate_source'
/Users/mfojtik/code/deltacloud/server/lib/deltacloud/../../views/drivers/index.xml.haml:2:in
`each'
/Users/mfojtik/code/deltacloud/server/lib/deltacloud/../../views/drivers/index.xml.haml:2:in
`evaluate_source'
I'll send another patch that will update the driver.
-- michal
> NACK
>
> This change breaks authentication.
> I see 'default' in other yaml files so I suppose it should be there.
>
> We have the following in several places in fgcp_client.rb and
> fgcp_driver.rb.
> They probably have to be updated at the same time.
>
> endpoint =
> Deltacloud::Drivers::driver_config[:fgcp][:entrypoints][country]
> unless
> endpoint
>
> Regards,
> Dies Koper
>
>
>> -----Original Message-----
>> From: mfojtik@redhat.com [mailto:mfojtik@redhat.com]
>> Sent: Wednesday, 16 May 2012 1:16 AM
>> To: dev@deltacloud.apache.org
>> Subject: [PATCH core 26/26] FGCP: Fixed configuration of providers
>>
>> From: Michal Fojtik <mf...@redhat.com>
>>
>>
>> Signed-off-by: Michal fojtik <mf...@redhat.com>
>> ---
>> server/config/drivers/fgcp.yaml | 1 +
>> 1 file changed, 1 insertion(+)
>>
>> diff --git a/server/config/drivers/fgcp.yaml
>> b/server/config/drivers/fgcp.yaml
>> index 8c24f66..838b710 100644
>> --- a/server/config/drivers/fgcp.yaml
>> +++ b/server/config/drivers/fgcp.yaml
>> @@ -4,6 +4,7 @@
>> :username: Username
>> :password: Secret Key Password of User Certificate
>> :entrypoints:
>> + default:
>> jp: https://api.oviss.jp.fujitsu.com/ovissapi/endpoint
>> au: https://api.globalcloud.fujitsu.com.au/ovissapi/endpoint
>> sg: https://api.globalcloud.sg.fujitsu.com/ovissapi/endpoint
>> --
>> 1.7.10.1
>>
RE: [PATCH core 26/26] FGCP: Fixed configuration of providers
Posted by "Koper, Dies" <di...@fast.au.fujitsu.com>.
Hi Michal,
That didn't work but if you use ['default'] instead of [:default], it
does.
Could you also update line 1151 of the driver (although commented out)
so that if we reactivate this code at some point in the future I won't
have to scratch my head why it won't work? :)
# Deltacloud::Drivers::driver_config[:fgcp][:entrypoints].keys
Cheers,
Dies Koper
> -----Original Message-----
> From: Michal Fojtik [mailto:mi@mifo.sk]
> Sent: Wednesday, 16 May 2012 5:37 PM
> To: dev@deltacloud.apache.org
> Subject: Re: [PATCH core 26/26] FGCP: Fixed configuration of providers
>
> On 16 May 2012, at 4:02, Koper, Dies wrote:
>
> Hi again :-)
>
> So the patch is here:
>
> https://github.com/mifo/deltacloud/commit/478607bd34aeb23d7e38671
> 14f59f9b0cafe1379
>
> Let me know if you're OK with this fix.
>
> Cheers,
> Michal
>
> > NACK
> >
> > This change breaks authentication.
> > I see 'default' in other yaml files so I suppose it should be there.
> >
> > We have the following in several places in fgcp_client.rb and
> > fgcp_driver.rb.
> > They probably have to be updated at the same time.
> >
> > endpoint =
> > Deltacloud::Drivers::driver_config[:fgcp][:entrypoints][country]
> > unless
> > endpoint
> >
> > Regards,
> > Dies Koper
> >
> >
> >> -----Original Message-----
> >> From: mfojtik@redhat.com [mailto:mfojtik@redhat.com]
> >> Sent: Wednesday, 16 May 2012 1:16 AM
> >> To: dev@deltacloud.apache.org
> >> Subject: [PATCH core 26/26] FGCP: Fixed configuration of providers
> >>
> >> From: Michal Fojtik <mf...@redhat.com>
> >>
> >>
> >> Signed-off-by: Michal fojtik <mf...@redhat.com>
> >> ---
> >> server/config/drivers/fgcp.yaml | 1 +
> >> 1 file changed, 1 insertion(+)
> >>
> >> diff --git a/server/config/drivers/fgcp.yaml
> >> b/server/config/drivers/fgcp.yaml
> >> index 8c24f66..838b710 100644
> >> --- a/server/config/drivers/fgcp.yaml
> >> +++ b/server/config/drivers/fgcp.yaml
> >> @@ -4,6 +4,7 @@
> >> :username: Username
> >> :password: Secret Key Password of User Certificate
> >> :entrypoints:
> >> + default:
> >> jp: https://api.oviss.jp.fujitsu.com/ovissapi/endpoint
> >> au: https://api.globalcloud.fujitsu.com.au/ovissapi/endpoint
> >> sg: https://api.globalcloud.sg.fujitsu.com/ovissapi/endpoint
> >> --
> >> 1.7.10.1
> >>
Re: [PATCH core 26/26] FGCP: Fixed configuration of providers
Posted by Michal Fojtik <mi...@mifo.sk>.
On 16 May 2012, at 4:02, Koper, Dies wrote:
Hi again :-)
So the patch is here:
https://github.com/mifo/deltacloud/commit/478607bd34aeb23d7e3867114f59f9b0cafe1379
Let me know if you're OK with this fix.
Cheers,
Michal
> NACK
>
> This change breaks authentication.
> I see 'default' in other yaml files so I suppose it should be there.
>
> We have the following in several places in fgcp_client.rb and
> fgcp_driver.rb.
> They probably have to be updated at the same time.
>
> endpoint =
> Deltacloud::Drivers::driver_config[:fgcp][:entrypoints][country]
> unless
> endpoint
>
> Regards,
> Dies Koper
>
>
>> -----Original Message-----
>> From: mfojtik@redhat.com [mailto:mfojtik@redhat.com]
>> Sent: Wednesday, 16 May 2012 1:16 AM
>> To: dev@deltacloud.apache.org
>> Subject: [PATCH core 26/26] FGCP: Fixed configuration of providers
>>
>> From: Michal Fojtik <mf...@redhat.com>
>>
>>
>> Signed-off-by: Michal fojtik <mf...@redhat.com>
>> ---
>> server/config/drivers/fgcp.yaml | 1 +
>> 1 file changed, 1 insertion(+)
>>
>> diff --git a/server/config/drivers/fgcp.yaml
>> b/server/config/drivers/fgcp.yaml
>> index 8c24f66..838b710 100644
>> --- a/server/config/drivers/fgcp.yaml
>> +++ b/server/config/drivers/fgcp.yaml
>> @@ -4,6 +4,7 @@
>> :username: Username
>> :password: Secret Key Password of User Certificate
>> :entrypoints:
>> + default:
>> jp: https://api.oviss.jp.fujitsu.com/ovissapi/endpoint
>> au: https://api.globalcloud.fujitsu.com.au/ovissapi/endpoint
>> sg: https://api.globalcloud.sg.fujitsu.com/ovissapi/endpoint
>> --
>> 1.7.10.1
>>
RE: [PATCH core 26/26] FGCP: Fixed configuration of providers
Posted by "Koper, Dies" <di...@fast.au.fujitsu.com>.
NACK
This change breaks authentication.
I see 'default' in other yaml files so I suppose it should be there.
We have the following in several places in fgcp_client.rb and
fgcp_driver.rb.
They probably have to be updated at the same time.
endpoint =
Deltacloud::Drivers::driver_config[:fgcp][:entrypoints][country] unless
endpoint
Regards,
Dies Koper
> -----Original Message-----
> From: mfojtik@redhat.com [mailto:mfojtik@redhat.com]
> Sent: Wednesday, 16 May 2012 1:16 AM
> To: dev@deltacloud.apache.org
> Subject: [PATCH core 26/26] FGCP: Fixed configuration of providers
>
> From: Michal Fojtik <mf...@redhat.com>
>
>
> Signed-off-by: Michal fojtik <mf...@redhat.com>
> ---
> server/config/drivers/fgcp.yaml | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/server/config/drivers/fgcp.yaml
> b/server/config/drivers/fgcp.yaml
> index 8c24f66..838b710 100644
> --- a/server/config/drivers/fgcp.yaml
> +++ b/server/config/drivers/fgcp.yaml
> @@ -4,6 +4,7 @@
> :username: Username
> :password: Secret Key Password of User Certificate
> :entrypoints:
> + default:
> jp: https://api.oviss.jp.fujitsu.com/ovissapi/endpoint
> au: https://api.globalcloud.fujitsu.com.au/ovissapi/endpoint
> sg: https://api.globalcloud.sg.fujitsu.com/ovissapi/endpoint
> --
> 1.7.10.1
>
[PATCH core 26/26] FGCP: Fixed configuration of providers
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/config/drivers/fgcp.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/server/config/drivers/fgcp.yaml b/server/config/drivers/fgcp.yaml
index 8c24f66..838b710 100644
--- a/server/config/drivers/fgcp.yaml
+++ b/server/config/drivers/fgcp.yaml
@@ -4,6 +4,7 @@
:username: Username
:password: Secret Key Password of User Certificate
:entrypoints:
+ default:
jp: https://api.oviss.jp.fujitsu.com/ovissapi/endpoint
au: https://api.globalcloud.fujitsu.com.au/ovissapi/endpoint
sg: https://api.globalcloud.sg.fujitsu.com/ovissapi/endpoint
--
1.7.10.1
[PATCH core 24/26] SBC: Renamed the driver class to match with common pattern
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/lib/deltacloud/drivers/sbc/sbc_client.rb | 2 +-
server/lib/deltacloud/drivers/sbc/sbc_driver.rb | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/server/lib/deltacloud/drivers/sbc/sbc_client.rb b/server/lib/deltacloud/drivers/sbc/sbc_client.rb
index a0e36cb..97e89ca 100644
--- a/server/lib/deltacloud/drivers/sbc/sbc_client.rb
+++ b/server/lib/deltacloud/drivers/sbc/sbc_client.rb
@@ -21,7 +21,7 @@ require 'digest/md5'
module Deltacloud
module Drivers
- module SBC
+ module Sbc
class FixtureNotFound < Exception; end
diff --git a/server/lib/deltacloud/drivers/sbc/sbc_driver.rb b/server/lib/deltacloud/drivers/sbc/sbc_driver.rb
index c7b109e..2e09a15 100644
--- a/server/lib/deltacloud/drivers/sbc/sbc_driver.rb
+++ b/server/lib/deltacloud/drivers/sbc/sbc_driver.rb
@@ -15,17 +15,17 @@
# under the License.
require 'nokogiri'
-require 'deltacloud/drivers/sbc/sbc_client'
+require_relative './sbc_client'
module Deltacloud
module Drivers
- module SBC
+ module Sbc
#
# Driver for the IBM Smart Business Cloud (SBC).
#
# 31 January 2011
#
-class SBCDriver < Deltacloud::BaseDriver
+class SbcDriver < Deltacloud::BaseDriver
#
# Retrieves images
#
--
1.7.10.1
[PATCH core 20/26] Core: Fixed 19 vs 18 backward compatibility issue
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/lib/deltacloud/drivers/base_driver.rb | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/server/lib/deltacloud/drivers/base_driver.rb b/server/lib/deltacloud/drivers/base_driver.rb
index f3637b7..3d4c75d 100644
--- a/server/lib/deltacloud/drivers/base_driver.rb
+++ b/server/lib/deltacloud/drivers/base_driver.rb
@@ -144,7 +144,8 @@ module Deltacloud
end
def has_capability?(method)
- (self.class.instance_methods - self.class.superclass.methods).include? method
+ method = (RUBY_VERSION =~ /^1\.9/) ? method : method.to_s
+ (self.class.instance_methods - self.class.superclass.instance_methods).include? method
end
## Capabilities
--
1.7.10.1
[PATCH core 02/26] Core: Added new Sinatra::Base Deltacloud collection based on Sinatra::Rabbit
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/config.ru | 44 +++-
server/lib/deltacloud/base_driver/base_driver.rb | 250 ------------------
server/lib/deltacloud/base_driver/exceptions.rb | 191 --------------
server/lib/deltacloud/base_driver/features.rb | 276 --------------------
server/lib/deltacloud/collections.rb | 54 ++++
server/lib/deltacloud/collections/addresses.rb | 83 ++++++
server/lib/deltacloud/collections/buckets.rb | 215 +++++++++++++++
server/lib/deltacloud/collections/drivers.rb | 51 ++++
server/lib/deltacloud/collections/firewalls.rb | 116 ++++++++
.../deltacloud/collections/hardware_profiles.rb | 27 ++
server/lib/deltacloud/collections/images.rb | 70 +++++
.../lib/deltacloud/collections/instance_states.rb | 57 ++++
server/lib/deltacloud/collections/instances.rb | 103 ++++++++
server/lib/deltacloud/collections/keys.rb | 61 +++++
.../lib/deltacloud/collections/load_balancers.rb | 85 ++++++
server/lib/deltacloud/collections/realms.rb | 27 ++
.../deltacloud/collections/storage_snapshots.rb | 51 ++++
.../lib/deltacloud/collections/storage_volumes.rb | 99 +++++++
server/lib/deltacloud/drivers/base_driver.rb | 265 +++++++++++++++++++
server/lib/deltacloud/drivers/exceptions.rb | 191 ++++++++++++++
server/lib/deltacloud/helpers.rb | 86 +++++-
server/lib/deltacloud/models.rb | 26 +-
server/lib/deltacloud/models/hardware_profile.rb | 194 ++++++++++++++
server/lib/deltacloud/models/state_machine.rb | 99 +++++++
server/lib/deltacloud/state_machine.rb | 115 --------
25 files changed, 1974 insertions(+), 862 deletions(-)
delete mode 100644 server/lib/deltacloud/base_driver/base_driver.rb
delete mode 100644 server/lib/deltacloud/base_driver/exceptions.rb
delete mode 100644 server/lib/deltacloud/base_driver/features.rb
create mode 100644 server/lib/deltacloud/collections.rb
create mode 100644 server/lib/deltacloud/collections/addresses.rb
create mode 100644 server/lib/deltacloud/collections/buckets.rb
create mode 100644 server/lib/deltacloud/collections/drivers.rb
create mode 100644 server/lib/deltacloud/collections/firewalls.rb
create mode 100644 server/lib/deltacloud/collections/hardware_profiles.rb
create mode 100644 server/lib/deltacloud/collections/images.rb
create mode 100644 server/lib/deltacloud/collections/instance_states.rb
create mode 100644 server/lib/deltacloud/collections/instances.rb
create mode 100644 server/lib/deltacloud/collections/keys.rb
create mode 100644 server/lib/deltacloud/collections/load_balancers.rb
create mode 100644 server/lib/deltacloud/collections/realms.rb
create mode 100644 server/lib/deltacloud/collections/storage_snapshots.rb
create mode 100644 server/lib/deltacloud/collections/storage_volumes.rb
create mode 100644 server/lib/deltacloud/drivers/base_driver.rb
create mode 100644 server/lib/deltacloud/drivers/exceptions.rb
create mode 100644 server/lib/deltacloud/models/hardware_profile.rb
create mode 100644 server/lib/deltacloud/models/state_machine.rb
delete mode 100644 server/lib/deltacloud/state_machine.rb
diff --git a/server/config.ru b/server/config.ru
index a1f9efd..f17de43 100644
--- a/server/config.ru
+++ b/server/config.ru
@@ -14,14 +14,46 @@
# License for the specific language governing permissions and limitations
# under the License.
-require 'rubygems'
+# The default URL prefix (where to mount Deltacloud API)
-$top_srcdir ||= File::expand_path(File.dirname(__FILE__))
+# The default driver is 'mock'
+ENV['API_DRIVER'] ||= 'mock'
-$:.unshift File.join($top_srcdir, 'lib')
+# Set the API frontend use ('cimi' or 'deltacloud', default is 'deltacloud')
+ENV['API_FRONTEND'] ||= 'deltacloud'
-server_dir = ENV['API_FRONTEND'] == 'cimi' ? 'cimi' : 'deltacloud'
+# We need to set different API version and entrypoint location
+if ENV['API_FRONTEND'] == 'deltacloud'
+ API_VERSION = "9.9.9"
+ API_ROOT_URL = "/api"
+elsif ENV['API_FRONTEND'] == 'cimi'
+ API_VERSION = "1.0.0"
+ API_ROOT_URL = "/cloudEntryPoint"
+end
-load File.join($top_srcdir, 'lib', server_dir, 'server.rb')
+#begin
+ require File.join(File.dirname(__FILE__), 'lib', ENV['API_FRONTEND'], 'server.rb')
+#rescue LoadError => e
+# puts "[ERROR] The specified frontend (#{ENV['API_FRONTEND']}) not supported (#{e.message})"
+# exit 1
+#end
-run Sinatra::Application
+if self.respond_to? :map
+ map "/" do
+ class IndexEntrypoint < Sinatra::Base
+ get "/" do
+ redirect API_ROOT_URL, 301
+ end
+ end
+ run IndexEntrypoint
+ end
+
+ map API_ROOT_URL do
+ use Rack::Static, :urls => ["/stylesheets", "/javascripts"], :root => "public"
+ use Rack::Session::Cookie
+ case ENV['API_FRONTEND']
+ when 'deltacloud' then run Rack::Cascade.new([Deltacloud::API])
+ when 'cimi' then run Rack::Cascade.new([CIMI::API])
+ end
+ end
+end
diff --git a/server/lib/deltacloud/base_driver/base_driver.rb b/server/lib/deltacloud/base_driver/base_driver.rb
deleted file mode 100644
index 01dd5e7..0000000
--- a/server/lib/deltacloud/base_driver/base_driver.rb
+++ /dev/null
@@ -1,250 +0,0 @@
-#
-# 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.
-
-require 'deltacloud/base_driver/exceptions'
-
-module Deltacloud
-
- class BaseDriver
-
- include ExceptionHandler
-
- STATE_MACHINE_OPTS = {
- :all_states => [:start, :pending, :running, :stopping, :stopped, :finish],
- :all_actions => [:create, :reboot, :stop, :start, :destroy]
- }
-
- def name
- self.class.name.split('::').last.gsub('Driver', '').downcase
- end
-
- def self.exceptions(&block)
- ExceptionHandler::exceptions(&block)
- end
-
- def self.define_hardware_profile(name,&block)
- @hardware_profiles ||= []
- hw_profile = @hardware_profiles.find{|e| e.name == name}
- return if hw_profile
- hw_profile = ::Deltacloud::HardwareProfile.new( name, &block )
- @hardware_profiles << hw_profile
- hw_params = hw_profile.params
- unless hw_params.empty?
- feature :instances, :hardware_profiles do
- decl.operation(:create) { add_params(hw_params) }
- end
- end
- end
-
- def self.hardware_profiles
- @hardware_profiles ||= []
- @hardware_profiles
- end
-
- def hardware_profiles(credentials, opts = nil)
- results = self.class.hardware_profiles
- filter_hardware_profiles(results, opts)
- end
-
- def hardware_profile(credentials, name)
- hardware_profiles(credentials, :id => name).first
- end
-
- def filter_hardware_profiles(profiles, opts)
- if opts
- if v = opts[:architecture]
- profiles = profiles.select { |hwp| hwp.include?(:architecture, v) }
- end
- # As a request param, we call 'name' 'id'
- if v = opts[:id]
- profiles = profiles.select { |hwp| hwp.name == v }
- end
- end
- profiles
- end
-
- def find_hardware_profile(credentials, name, image_id)
- hwp = nil
- if name
- unless hwp = hardware_profiles(credentials, :id => name).first
- raise BackendError.new(400, "bad-hardware-profile-name",
- "Hardware profile '#{name}' does not exist", nil)
- end
- else
- unless image = image(credentials, :id=>image_id)
- raise BackendError.new(400, "bad-image-id",
- "Image with ID '#{image_id}' does not exist", nil)
- end
- hwp = hardware_profiles(credentials,
- :architecture=>image.architecture).first
- end
- return hwp
- end
-
- def self.define_instance_states(&block)
- machine = ::Deltacloud::StateMachine.new(STATE_MACHINE_OPTS, &block)
- @instance_state_machine = machine
- end
-
- def self.instance_state_machine
- @instance_state_machine
- end
-
- def instance_state_machine
- self.class.instance_state_machine
- end
-
- def instance_actions_for(state)
- actions = []
- state_key = state.downcase.to_sym
- states = instance_state_machine.states()
- current_state = states.find{|e| e.name == state.underscore.to_sym }
- if ( current_state )
- actions = current_state.transitions.collect{|e|e.action}
- actions.reject!{|e| e.nil?}
- end
- actions
- end
-
- ## Capabilities
- # The rabbit dsl supports declaring a capability that is required
- # in the backend driver for the call to succeed. A driver can
- # provide a capability by implementing the method with the same
- # name as the capability. Below is a list of the capabilities as
- # the expected method signatures.
- #
- # Following the capability list are the resource member show
- # methods. They each require that the corresponding collection
- # method be defined
- #
- # TODO: standardize all of these to the same signature (credentials, opts)
- #
- # def realms(credentials, opts=nil)
- #
- # def images(credentials, ops)
- #
- # def instances(credentials, ops)
- # def create_instance(credentials, image_id, opts)
- # def start_instance(credentials, id)
- # def stop_instance(credentials, id)
- # def reboot_instance(credentials, id)
- #
- # def storage_volumes(credentials, ops)
- #
- # def storage_snapshots(credentials, ops)
- #
- # def buckets(credentials, opts = nil)
- # def create_bucket(credentials, name, opts=nil)
- # def delete_bucket(credentials, name, opts=nil)
- #
- # def blobs(credentials, opts = nil)
- # def blob_data(credentials, bucket_id, blob_id, opts)
- # def create_blob(credentials, bucket_id, blob_id, blob_data, opts=nil)
- # def delete_blob(credentials, bucket_id, blob_id, opts=nil)
- #
- # def keys(credentials, opts)
- # def create_key(credentials, opts)
- # def destroy_key(credentials, opts)
- #
- # def firewalls(credentials, opts)
- # def create_firewall(credentials, opts)
- # def delete_firewall(credentials, opts)
- # def create_firewall_rule(credentials, opts)
- # def delete_firewall_rule(credentials, opts)
- # def providers(credentials)
- def realm(credentials, opts)
- realms = realms(credentials, opts).first if has_capability?(:realms)
- end
-
- def image(credentials, opts)
- images(credentials, opts).first if has_capability?(:images)
- end
-
- def instance(credentials, opts)
- instances(credentials, opts).first if has_capability?(:instances)
- end
-
- def storage_volume(credentials, opts)
- storage_volumes(credentials, opts).first if has_capability?(:storage_volumes)
- end
-
- def storage_snapshot(credentials, opts)
- storage_snapshots(credentials, opts).first if has_capability?(:storage_snapshots)
- end
-
- def bucket(credentials, opts = {})
- #list of objects within bucket
- buckets(credentials, opts).first if has_capability?(:buckets)
- end
-
- def blob(credentials, opts = {})
- blobs(credentials, opts).first if has_capability?(:blobs)
- end
-
- def key(credentials, opts=nil)
- keys(credentials, opts).first if has_capability?(:keys)
- end
-
- def firewall(credentials, opts={})
- firewalls(credentials, opts).first if has_capability?(:firewalls)
- end
-
- MEMBER_SHOW_METHODS =
- [ :realm, :image, :instance, :storage_volume, :bucket, :blob, :key, :firewall ]
-
- def has_capability?(capability)
- if MEMBER_SHOW_METHODS.include?(capability.to_sym)
- has_capability?(capability.to_s.pluralize)
- else
- respond_to?(capability)
- end
- end
-
- def filter_on(collection, attribute, opts)
- return collection if opts.nil?
- return collection if opts[attribute].nil?
- filter = opts[attribute]
- if ( filter.is_a?( Array ) )
- return collection.select{|e| filter.include?( e.send(attribute) ) }
- else
- return collection.select{|e| filter == e.send(attribute) }
- end
- end
-
- def supported_collections
- DEFAULT_COLLECTIONS
- end
-
- def has_collection?(collection)
- supported_collections.include?(collection)
- end
-
- def catched_exceptions_list
- { :error => [], :auth => [], :glob => [] }
- end
-
- def api_provider
- Thread.current[:provider] || ENV['API_PROVIDER']
- end
-
- # Return an array of the providers statically configured
- # in the driver's YAML file
- def configured_providers
- []
- end
- end
-
-end
diff --git a/server/lib/deltacloud/base_driver/exceptions.rb b/server/lib/deltacloud/base_driver/exceptions.rb
deleted file mode 100644
index a89b05f..0000000
--- a/server/lib/deltacloud/base_driver/exceptions.rb
+++ /dev/null
@@ -1,191 +0,0 @@
-# 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
- module ExceptionHandler
-
- class DeltacloudException < StandardError
-
- attr_accessor :code, :name, :message, :backtrace, :request
-
- def initialize(code, name, message, backtrace, request=nil)
- @code, @name, @message = code, name, message
- @backtrace = backtrace
- @request = request
- self
- end
-
- end
-
- class AuthenticationFailure < DeltacloudException
- def initialize(e, message=nil)
- message ||= e.message
- super(401, e.class.name, message, e.backtrace)
- end
- end
-
- class UnknownMediaTypeError < DeltacloudException
- def initialize(e, message=nil)
- message ||= e.message
- super(406, e.class.name, message, e.backtrace)
- end
- end
-
- class MethodNotAllowed < DeltacloudException
- def initialize(e, message=nil)
- message ||= e.message
- super(405, e.class.name, message, e.backtrace)
- end
- end
-
- class ValidationFailure < DeltacloudException
- def initialize(e, message=nil)
- message ||= e.message
- super(400, e.class.name, message, e.backtrace)
- end
- end
-
- class BackendError < DeltacloudException
- def initialize(e, message=nil)
- message ||= e.message
- super(500, e.class.name, message, e.backtrace, message)
- end
- end
-
- class ProviderError < DeltacloudException
- def initialize(e, message)
- message ||= e.message
- super(502, e.class.name, message, e.backtrace)
- end
- end
-
- class ProviderTimeout < DeltacloudException
- def initialize(e, message)
- message ||= e.message
- super(504, e.class.name, message, e.backtrace)
- end
- end
-
- class NotImplemented < DeltacloudException
- def initialize(e, message)
- message ||= e.message
- super(501, e.class.name, message, e.backtrace)
- end
- end
-
- class ObjectNotFound < DeltacloudException
- def initialize(e, message)
- message ||= e.message
- super(404, e.class.name, message, e.backtrace)
- end
- end
-
- class NotSupported < DeltacloudException
- def initialize(message)
- super(501, self.class.name, message, self.backtrace)
- end
- end
-
- class ExceptionDef
- attr_accessor :status
- attr_accessor :message
- attr_reader :conditions
- attr_reader :handler
-
- def initialize(conditions, &block)
- @conditions = conditions
- instance_eval(&block) if block_given?
- end
-
- def status(code)
- self.status = code
- end
-
- def message(message)
- self.message = message
- end
-
- def exception(handler)
- self.handler = handler
- end
-
- # Condition can be class or regexp
- #
- def match?(e)
- @conditions.each do |c|
- return true if c.class == Class && e.class == c
- return true if c.class == Regexp && (e.class.name =~ c or e.message =~ c)
- end
- return false
- end
-
- def handler(e)
- return @handler if @handler
- case @status
- when 401 then Deltacloud::ExceptionHandler::AuthenticationFailure.new(e, @message)
- when 404 then Deltacloud::ExceptionHandler::ObjectNotFound.new(e, @message)
- when 406 then Deltacloud::ExceptionHandler::UnknownMediaTypeError.new(e, @message)
- when 405 then Deltacloud::ExceptionHandler::MethodNotAllowed.new(e, @message)
- when 400 then Deltacloud::ExceptionHandler::ValidationFailure.new(e, @message)
- when 500 then Deltacloud::ExceptionHandler::BackendError.new(e, @message)
- when 501 then Deltacloud::ExceptionHandler::NotImplemented.new(e, @message)
- when 502 then Deltacloud::ExceptionHandler::ProviderError.new(e, @message)
- when 504 then Deltacloud::ExceptionHandler::ProviderTimeout.new(e, @message)
- end
- end
-
- end
-
- class Exceptions
- attr_reader :exception_definitions
-
- def initialize(&block)
- @exception_definitions = []
- instance_eval(&block) if block_given?
- self
- end
-
- def on(*conditions, &block)
- @exception_definitions << ExceptionDef::new(conditions, &block) if block_given?
- end
- end
-
- def self.exceptions(&block)
- @definitions = Exceptions.new(&block).exception_definitions if block_given?
- @definitions
- end
-
- def safely(&block)
- begin
- block.call
- rescue
- report_method = $stderr.respond_to?(:err) ? :err : :puts
- Deltacloud::ExceptionHandler::exceptions.each do |exdef|
- if exdef.match?($!)
- new_exception = exdef.handler($!)
- m = new_exception.message.nil? ? $!.message : new_exception.message
- $stderr.send(report_method, "#{[$!.class.to_s, m].join(':')}\n#{$!.backtrace[0..10].join("\n")}")
- raise exdef.handler($!) unless new_exception.nil?
- end
- end
- $stderr.send(report_method, "[NO HANDLED] #{[$!.class.to_s, $!.message].join(': ')}\n#{$!.backtrace.join("\n")}")
- raise Deltacloud::ExceptionHandler::BackendError.new($!, "Unhandled exception or status code (#{$!.message})")
- end
- end
-
- end
-
-end
diff --git a/server/lib/deltacloud/base_driver/features.rb b/server/lib/deltacloud/base_driver/features.rb
deleted file mode 100644
index 37e5ef0..0000000
--- a/server/lib/deltacloud/base_driver/features.rb
+++ /dev/null
@@ -1,276 +0,0 @@
-#
-# 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.
-
-require 'deltacloud/validation'
-
-# Add advertising of optional features to the base driver
-module Deltacloud
-
- class FeatureError < StandardError; end
- class DuplicateFeatureDeclError < FeatureError; end
- class UndeclaredFeatureError < FeatureError; end
-
- class BaseDriver
-
- # An operation on a collection like cretae or show. Features
- # can add parameters to operations
- class Operation
- attr_reader :name
-
- include Deltacloud::Validation
-
- def initialize(name, &block)
- @name = name
- @params = {}
- instance_eval &block
- end
- end
-
- # The declaration of a feature, defines what operations
- # are modified by it
- class FeatureDecl
- attr_reader :name, :operations
-
- def initialize(name, &block)
- @name = name
- @operations = []
- instance_eval &block
- end
-
- def description(text=nil)
- @description = text if text
- @description
- end
-
- # Add/modify an operation or look up an existing one. If +block+ is
- # provided, create a new operation if none exists with name
- # +name+. Evaluate the +block+ against this instance. If no +block+
- # is provided, look up the operation with name +name+
- def operation(name, &block)
- op = @operations.find { |op| op.name == name }
- if block_given?
- if op.nil?
- op = Operation.new(name, &block)
- @operations << op
- else
- op.instance_eval(&block)
- end
- end
- op
- end
- end
-
- # A specific feature enabled by a driver (see +feature+)
- class Feature
- attr_reader :decl, :constraints
-
- def initialize(decl, &block)
- @decl = decl
- @constraints = {}
- instance_eval &block if block_given?
- end
-
- def name
- decl.name
- end
-
- def operations
- decl.operations
- end
-
- def description
- decl.description
- end
-
- def constraint(name, value)
- @constraints[name] = value
- end
- end
-
- def self.feature_decls
- @@feature_decls ||= {}
- end
-
- def self.feature_decl_for(collection, name)
- decls = feature_decls[collection]
- if decls
- decls.find { |dcl| dcl.name == name }
- else
- nil
- end
- end
-
- # Declare a new feature
- def self.declare_feature(collection, name, &block)
- feature_decls[collection] ||= []
- raise DuplicateFeatureDeclError if feature_decl_for(collection, name)
- feature_decls[collection] << FeatureDecl.new(name, &block)
- end
-
- def self.features
- @features ||= {}
- end
-
- # Declare in a driver that it supports a specific feature
- #
- # The same feature can be declared multiple times in a driver, so that
- # it can be changed successively by passing in different blocks.
- def self.feature(collection, name, &block)
- features[collection] ||= []
- if f = features[collection].find { |f| f.name == name }
- f.instance_eval(&block) if block_given?
- return f
- end
- unless decl = feature_decl_for(collection, name)
- raise UndeclaredFeatureError, "No feature #{name} for #{collection}"
- end
- features[collection] << Feature.new(decl, &block)
- end
-
- def features(collection)
- self.class.features[collection] || []
- end
-
- def features_for_operation(collection, operation)
- features(collection).select do |f|
- f.operations.detect { |o| o.name == operation }
- end
- end
-
- #
- # Declaration of optional features
- #
- declare_feature :images, :owner_id do
- description "Filter images using owner id"
- operation :index do
- param :owner_id, :string, :optional, [], "Owner ID"
- end
- end
-
- declare_feature :images, :user_name do
- description "Allow specifying user name for created image"
- operation :create do
- param :name, :string, :optional, [], "Image name"
- end
- end
-
- declare_feature :images, :user_description do
- description "Allow specifying user description for created image"
- operation :create do
- param :description, :string, :optional, [], "Image description"
- end
- end
-
- declare_feature :instances, :user_name do
- description "Accept a user-defined name on instance creation"
- operation :create do
- param :name, :string, :optional, [], "The user-defined name"
- end
- end
-
- declare_feature :instances, :user_data do
- description "Make user-defined data available on a special webserver"
- operation :create do
- param :user_data, :string, :optional, [],
- "Base64 encoded user data will be published to internal webserver"
- end
- end
-
- declare_feature :instances, :user_iso do
- description "Make user-defined ISO available inside instance"
- operation :create do
- param :user_iso, :string, :optional, [],
- "Base64 encoded gzipped ISO file will be accessible as CD-ROM drive in instance"
- end
- end
-
- declare_feature :instances, :user_files do
- description "Accept up to 5 files to be placed into the instance before launch."
- operation :create do
- 1.upto(5) do |i|
- param :"path#{i}", :string, :optional, [],
- "Path where to place the #{i.ordinalize} file, up to 255 characters"
- param :"content#{i}", :string, :optional, nil,
- "Contents for the #{i.ordinalize} file, up to 10 kB, Base64 encoded"
- end
- end
- end
-
- declare_feature :instances, :firewalls do
- description "Put instance in one or more firewalls (security groups) on launch"
- operation :create do
- param :firewalls, :array, :optional, nil, "Array of firewall ID strings"
- "Array of firewall (security group) id"
- end
- end
-
- declare_feature :instances, :authentication_key do
- operation :create do
- param :keyname, :string, :optional, [], "Key authentification method"
- end
- operation :show do
- end
- end
-
- declare_feature :instances, :authentication_password do
- operation :create do
- param :password, :string, :optional
- end
- end
-
- declare_feature :instances, :hardware_profiles do
- description "Size instances according to changes to a hardware profile"
- # The parameters are filled in from the hardware profiles
- end
-
- declare_feature :buckets, :bucket_location do
- description "Take extra location parameter for Bucket creation (e.g. S3, 'eu' or 'us-west-1')"
- operation :create do
- param :location, :string, :optional
- end
- end
-
- declare_feature :instances, :register_to_load_balancer do
- description "Register instance to load balancer"
- operation :create do
- param :load_balancer_id, :string, :optional
- end
- end
-
- declare_feature :instances, :instance_count do
- description "Number of instances to be launch with at once"
- operation :create do
- param :instance_count, :string, :optional
- end
- end
-
- declare_feature :instances, :attach_snapshot do
- description "Attach an snapshot to instance on create"
- operation :create do
- param :snapshot_id, :string, :optional
- param :device_name, :string, :optional
- end
- end
-
- declare_feature :instances, :sandboxing do
- description "Allow lanuching sandbox images"
- operation :create do
- param :sandbox, :string, :optional
- end
- end
-
- end
-end
diff --git a/server/lib/deltacloud/collections.rb b/server/lib/deltacloud/collections.rb
new file mode 100644
index 0000000..2363887
--- /dev/null
+++ b/server/lib/deltacloud/collections.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
+
+ def self.collection_names
+ @collections.map { |c| c.collection_name }
+ end
+
+ def self.collections
+ @collections ||= []
+ end
+
+ module Collections
+
+ def self.collection(name)
+ Deltacloud.collections.find { |c| c.collection_name == name }
+ end
+
+ def self.deltacloud_modules
+ @deltacloud_modules ||= []
+ end
+
+ Dir[File.join(File::dirname(__FILE__), "collections", "*.rb")].each do |collection|
+ require collection
+ base_collection_name = File.basename(collection).gsub('.rb', '')
+ deltacloud_module_class = Deltacloud::Collections.const_get(base_collection_name.camelize)
+ deltacloud_modules << deltacloud_module_class
+ deltacloud_module_class.collections.each do |c|
+ Deltacloud.collections << c
+ end unless deltacloud_module_class.collections.nil?
+ end
+
+ def self.included(klass)
+ klass.class_eval do
+ Deltacloud::Collections.deltacloud_modules.each { |c| use c }
+ end
+ end
+
+ end
+end
diff --git a/server/lib/deltacloud/collections/addresses.rb b/server/lib/deltacloud/collections/addresses.rb
new file mode 100644
index 0000000..b97d170
--- /dev/null
+++ b/server/lib/deltacloud/collections/addresses.rb
@@ -0,0 +1,83 @@
+# 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::Collections
+ class Addresses < Base
+
+ check_capability :for => lambda { |m| driver.respond_to? m }
+
+ collection :addresses do
+ description "Pool of IP addresses allocated in cloud provider"
+
+ standard_index_operation
+ standard_show_operation
+
+ 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, {})
+ status 201 # Created
+ response['Location'] = address_url(@address.id)
+ respond_to do |format|
+ format.xml { haml :"addresses/show", :ugly => true }
+ format.html { haml :"addresses/_address", :layout => false }
+ format.json { convert_to_json(:address, @address) }
+ end
+ end
+ end
+
+ operation :destroy, :with_capability => :destroy_address do
+ control do
+ driver.destroy_address(credentials, { :id => params[:id]})
+ status 204
+ respond_to do |format|
+ format.xml
+ format.json
+ format.html { redirect(addresses_url) }
+ end
+ end
+ end
+
+ action :associate, :with_capability => :associate_address do
+ description "Associate an IP address to an instance"
+ param :instance_id, :string, :required
+ control do
+ driver.associate_address(credentials, { :id => params[:id], :instance_id => params[:instance_id]})
+ status 202 # Accepted
+ respond_to do |format|
+ format.xml
+ format.json
+ format.html { redirect(address_url(params[:id])) }
+ end
+ end
+ end
+
+ action :disassociate, :with_capability => :associate_address do
+ description "Disassociate an IP address from an instance"
+ control do
+ driver.disassociate_address(credentials, { :id => params[:id] })
+ status 202 # Accepted
+ respond_to do |format|
+ format.xml
+ format.json
+ format.html { redirect(address_url(params[:id])) }
+ end
+ end
+ end
+
+ end
+
+ end
+end
diff --git a/server/lib/deltacloud/collections/buckets.rb b/server/lib/deltacloud/collections/buckets.rb
new file mode 100644
index 0000000..044bd6a
--- /dev/null
+++ b/server/lib/deltacloud/collections/buckets.rb
@@ -0,0 +1,215 @@
+# 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::Collections
+ class Buckets < Base
+ check_capability :for => lambda { |m| driver.respond_to? m }
+ check_features :for => lambda { |c, f| driver.class.has_feature?(c, f) }
+
+ collection :buckets do
+
+ collection :blobs, :with_id => :blob_id, :no_member => true do
+
+ operation :show, :with_capability => :blob do
+ control do
+ @blob = driver.blob(credentials, { :id => params[:blob_id], 'bucket' => params[:id]} )
+ if @blob
+ respond_to do |format|
+ format.xml { haml :"blobs/show" }
+ format.html { haml :"blobs/show" }
+ format.json { convert_to_json(:blob, @blob) }
+ end
+ else
+ report_error(404)
+ end
+ end
+
+ end
+
+ operation :create, :with_capability => :create_blob do
+ description "Create new blob"
+ param :blob_id, :string, :required
+ param :blob_data, :hash, :required
+ control do
+ bucket_id = params[:id]
+ blob_id = params['blob_id']
+ blob_data = params['blob_data']
+ user_meta = {}
+ #metadata from params (i.e., passed by http form post, e.g. browser)
+ max = params[:meta_params]
+ if(max)
+ (1..max.to_i).each do |i|
+ key = params[:"meta_name#{i}"]
+ key = "HTTP_X_Deltacloud_Blobmeta_#{key}"
+ value = params[:"meta_value#{i}"]
+ user_meta[key] = value
+ end
+ end
+ @blob = driver.create_blob(credentials, bucket_id, blob_id, blob_data, user_meta)
+ respond_to do |format|
+ format.xml { haml :"blobs/show" }
+ format.html { haml :"blobs/show"}
+ format.json {convert_to_json(:blob, @blob)}
+ end
+ end
+ end
+
+ operation :destroy, :with_capability => :delete_blob do
+ control do
+ bucket_id = params[:id]
+ blob_id = params[:blob_id]
+ driver.delete_blob(credentials, bucket_id, blob_id)
+ status 204
+ respond_to do |format|
+ format.xml
+ format.json
+ format.html { redirect(bucket_url(bucket_id)) }
+ end
+ end
+ end
+
+ action :stream, :http_method => :put, :with_capability => :create_blob do
+ description "Stream new blob data into the blob"
+ control do
+ 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],
+ 'bucket' => params[:bucket]})
+ respond_to do |format|
+ format.xml { haml :"blobs/show" }
+ format.html { haml :"blobs/show" }
+ format.json { convert_to_json(:blob, @blob) }
+ end
+ elsif(env["BLOB_FAIL"])
+ report_error(500) #OK?
+ else # small blobs - < 112kb dont hit the streaming monkey patch - use 'normal' create_blob
+ # also, if running under webrick don't hit the streaming patch (Thin specific)
+ bucket_id = params[:bucket]
+ blob_id = params[:blob]
+ temp_file = Tempfile.new("temp_blob_file")
+ temp_file.write(env['rack.input'].read)
+ temp_file.flush
+ 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)
+ temp_file.delete
+ respond_to do |format|
+ format.xml { haml :"blobs/show" }
+ format.html { haml :"blobs/show" }
+ format.json { convert_to_json(:blob, @blob) }
+ end
+ end
+ end
+ end
+
+ action :metadata, :http_method => :head, :with_capability => :blob_metadata do
+ control do
+ @blob_id = params[:blob]
+ @blob_metadata = driver.blob_metadata(credentials, {:id => params[:blob], 'bucket' => params[:bucket]})
+ if @blob_metadata
+ @blob_metadata.each do |k,v|
+ headers["X-Deltacloud-Blobmeta-#{k}"] = v
+ end
+ status 204
+ respond_to do |format|
+ format.xml
+ format.json
+ end
+ else
+ report_error(404)
+ end
+ end
+ end
+
+ action :update, :http_method => :post, :with_capability => :update_blob_metadata do
+ control do
+ meta_hash = BlobHelper::extract_blob_metadata_hash(request.env)
+ success = driver.update_blob_metadata(credentials, {'bucket'=>params[:bucket], :id =>params[:blob], 'meta_hash' => meta_hash})
+ if(success)
+ meta_hash.each do |k,v|
+ headers["X-Deltacloud-Blobmeta-#{k}"] = v
+ end
+ status 204
+ respond_to do |format|
+ format.xml
+ format.json
+ end
+ else
+ report_error(404) #FIXME is this the right error code?
+ end
+ end
+ end
+
+ action :content, :http_method => :get, :with_capability => :blob do
+ description "Download blob content"
+ control do
+ @blob = driver.blob(credentials, { :id => params[:blob], 'bucket' => params[:bucket]})
+ if @blob
+ params['content_length'] = @blob.content_length
+ params['content_type'] = @blob.content_type
+ params['content_disposition'] = "attachment; filename=#{@blob.id}"
+ BlobStream.call(env, credentials, params)
+ else
+ report_error(404)
+ end
+ end
+ end
+
+ end
+
+ get route_for('/buckets/new') do
+ respond_to do |format|
+ format.html { haml :"buckets/new" }
+ end
+ end
+
+ standard_show_operation
+ standard_index_operation
+
+ operation :create, :with_capability => :create_bucket do
+ param :name, :string, :required
+ control do
+ @bucket = driver.create_bucket(credentials, params[:name], params)
+ status 201
+ response['Location'] = bucket_url(@bucket.id)
+ respond_to do |format|
+ format.xml { haml :"buckets/show" }
+ format.json { convert_to_json(:bucket, @bucket) }
+ format.html do
+ redirect bucket_url(@bucket.id) if @bucket and @bucket.id
+ redirect buckets_url
+ end
+ end
+ end
+ end
+
+ operation :destroy, :with_capability => :delete_bucket do
+ control do
+ driver.delete_bucket(credentials, params[:id], params)
+ status 204
+ respond_to do |format|
+ format.xml
+ format.json
+ format.html { redirect(buckets_url) }
+ end
+ end
+ end
+
+ end
+
+ end
+end
diff --git a/server/lib/deltacloud/collections/drivers.rb b/server/lib/deltacloud/collections/drivers.rb
new file mode 100644
index 0000000..41f324a
--- /dev/null
+++ b/server/lib/deltacloud/collections/drivers.rb
@@ -0,0 +1,51 @@
+# 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::Collections
+ class Drivers < Base
+
+ collection :drivers do
+
+ operation :index do
+ control do
+ @drivers = Deltacloud::Drivers.driver_config
+ respond_to do |format|
+ format.xml { haml :"drivers/index" }
+ format.json { @drivers.to_json }
+ format.html { haml :"drivers/index" }
+ end
+ end
+ end
+
+ operation :show do
+ control do
+ @name = params[:id].to_sym
+ if driver_symbol == @name
+ @providers = driver.providers(credentials) if driver.respond_to? :providers
+ end
+ @driver = Deltacloud::Drivers.driver_config[@name]
+ halt 404 unless @driver
+ respond_to do |format|
+ format.xml { haml :"drivers/show" }
+ format.json { @driver.to_json }
+ format.html { haml :"drivers/show" }
+ end
+ end
+ end
+
+ end
+
+ end
+end
diff --git a/server/lib/deltacloud/collections/firewalls.rb b/server/lib/deltacloud/collections/firewalls.rb
new file mode 100644
index 0000000..0a4242a
--- /dev/null
+++ b/server/lib/deltacloud/collections/firewalls.rb
@@ -0,0 +1,116 @@
+# 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::Collections
+ class Firewalls < Base
+
+ check_capability :for => lambda { |m| driver.respond_to? m }
+ check_features :for => lambda { |c, f| driver.class.has_feature?(c, f) }
+
+ get route_for('/firewalls/:id/new_rule') do
+ @firewall_name = params[:id]
+ respond_to do |format|
+ format.html {haml :"firewalls/new_rule" }
+ end
+ end
+
+ new_route_for :firewalls
+
+ collection :firewalls do
+ description "Allow user to define firewall rules for an instance (ec2 security groups) eg expose ssh access [port 22, tcp]."
+
+ collection :rules, :with_id => :rule_id, :no_member => true do
+
+ operation :destroy, :with_capability => :delete_firewall_rule do
+ control do
+ opts = {}
+ opts[:firewall] = params[:id]
+ opts[:rule_id] = params[:rule_id]
+ driver.delete_firewall_rule(credentials, opts)
+ status 204
+ respond_to do |format|
+ format.xml
+ format.json
+ format.html {redirect firewall_url(params[:id])}
+ end
+ end
+ end
+
+ end
+
+ standard_show_operation
+ standard_index_operation
+
+ operation :create, :with_capability => :create_firewall do
+ param :name, :string, :required
+ param :description, :string, :required
+ control do
+ @firewall = driver.create_firewall(credentials, params )
+ status 201 # Created
+ response['Location'] = firewall_url(@firewall.id)
+ respond_to do |format|
+ format.xml { haml :"firewalls/show" }
+ format.html { haml :"firewalls/show" }
+ format.json { convert_to_json(:firewall, @firewall) }
+ end
+ end
+ end
+
+ operation :destroy, :with_capability => :delete_firewall do
+ control do
+ driver.delete_firewall(credentials, params)
+ status 204
+ respond_to do |format|
+ format.xml
+ format.json
+ format.html { redirect(firewalls_url) }
+ end
+ end
+ end
+
+ action :rules, :with_capability => :create_firewall_rule do
+ param :protocol, :required, :string, ['tcp','udp','icmp'], "Transport layer protocol for the rule"
+ param :port_from, :required, :string, [], "Start of port range for the rule"
+ param :port_to, :required, :string, [], "End of port range for the rule"
+ control do
+ #source IPs from params
+ addresses = params.inject([]){|result,current| result << current.last unless current.grep(/^ip[-_]address/i).empty?; result}
+ #source groups from params
+ groups = {}
+ max_groups = params.select{|k,v| k=~/^group/}.size/2
+ for i in (1..max_groups) do
+ groups.merge!({params["group#{i}"]=>params["group#{i}owner"]})
+ end
+ params['addresses'] = addresses
+ params['groups'] = groups
+ if addresses.empty? && groups.empty?
+ raise Deltacloud::ExceptionHandler::ValidationFailure.new(
+ StandardError.new("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]})
+ status 201
+ respond_to do |format|
+ format.xml { haml :"firewalls/show" }
+ format.html { haml :"firewalls/show" }
+ format.json { convert_to_json(:firewall, @firewall) }
+ end
+ end
+ end
+
+ end
+ end
+end
diff --git a/server/lib/deltacloud/collections/hardware_profiles.rb b/server/lib/deltacloud/collections/hardware_profiles.rb
new file mode 100644
index 0000000..ff01d4a
--- /dev/null
+++ b/server/lib/deltacloud/collections/hardware_profiles.rb
@@ -0,0 +1,27 @@
+# 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::Collections
+ class HardwareProfiles < Base
+
+ collection :hardware_profiles do
+
+ standard_index_operation
+ standard_show_operation
+
+ end
+
+ end
+end
diff --git a/server/lib/deltacloud/collections/images.rb b/server/lib/deltacloud/collections/images.rb
new file mode 100644
index 0000000..c8b3e08
--- /dev/null
+++ b/server/lib/deltacloud/collections/images.rb
@@ -0,0 +1,70 @@
+# 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::Collections
+ class Images < Base
+ check_capability :for => lambda { |m| driver.respond_to? m }
+ check_features :for => lambda { |c, f| driver.class.has_feature?(c, f) }
+
+ new_route_for :images do
+ @instance = Instance.new( :id => params[:instance_id] )
+ status 404 unless @instance
+ end
+
+ collection :images do
+ description "Within a cloud provider a realm represents a boundary containing resources"
+
+ operation :index, :with_capability => :images do
+ param :architecture, :string, :optional
+ control { filter_all(:images) }
+ end
+
+ operation :show, :with_capability => :image do
+ control { show(:image) }
+ end
+
+ operation :create, :with_capability => :create_image do
+ param :instance_id, :string, :required
+ control do
+ @image = driver.create_image(credentials, {
+ :id => params[:instance_id],
+ :name => params[:name],
+ :description => params[:description]
+ })
+ status 201 # Created
+ response['Location'] = image_url(@image.id)
+ respond_to do |format|
+ format.xml { haml :"images/show" }
+ format.json { xml_to_json('images/show') }
+ format.html { haml :"images/show" }
+ end
+ end
+ end
+
+ operation :destroy, :with_capability => :destroy_image do
+ control do
+ driver.destroy_image(credentials, params[:id])
+ respond_to do |format|
+ format.xml { status 204 }
+ format.json { status 204 }
+ format.html { redirect(images_url) }
+ end
+ end
+ end
+
+ end
+
+ end
+end
diff --git a/server/lib/deltacloud/collections/instance_states.rb b/server/lib/deltacloud/collections/instance_states.rb
new file mode 100644
index 0000000..56c1561
--- /dev/null
+++ b/server/lib/deltacloud/collections/instance_states.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::Collections
+ class InstanceStates < Base
+
+ collection :instance_states do
+ operation :index do
+ control do
+ @machine = driver.instance_state_machine
+ respond_to do |format|
+ format.xml { haml :'instance_states/show', :layout => false }
+ format.json do
+ out = []
+ @machine.states.each do |state|
+ transitions = state.transitions.collect do |t|
+ t.automatically? ? {:to => t.destination, :auto => 'true'} : {:to => t.destination, :action => t.action}
+ end
+ out << { :name => state, :transitions => transitions }
+ end
+ out.to_json
+ end
+ format.html { haml :'instance_states/show'}
+ format.gv { erb :"instance_states/show" }
+ format.png do
+ # Trick respond_to into looking up the right template for the
+ # graphviz file
+ gv = erb(:"instance_states/show")
+ png = ''
+ cmd = 'dot -Kdot -Gpad="0.2,0.2" -Gsize="5.0,8.0" -Gdpi="180" -Tpng'
+ Open3.popen3( cmd ) do |stdin, stdout, stderr|
+ stdin.write( gv )
+ stdin.close()
+ png = stdout.read
+ end
+ content_type 'image/png'
+ png
+ end
+ end
+ end
+ end
+ end
+
+ end
+end
diff --git a/server/lib/deltacloud/collections/instances.rb b/server/lib/deltacloud/collections/instances.rb
new file mode 100644
index 0000000..5202149
--- /dev/null
+++ b/server/lib/deltacloud/collections/instances.rb
@@ -0,0 +1,103 @@
+# 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::Collections
+ class Instances < Base
+
+ include Deltacloud::InstanceFeatures
+
+ check_capability :for => lambda { |m| driver.respond_to? m }
+ check_features :for => lambda { |c, f| driver.class.has_feature?(c, f) }
+
+ new_route_for(:instances) do
+ @instance = Instance.new( { :id=>params[:id], :image_id=>params[:image_id] } )
+ @image = Image.new( :id => params[:image_id] )
+ @hardware_profiles = driver.hardware_profiles(credentials, :architecture => @image.architecture )
+ @realms = [Realm.new(:id => params[:realm_id])] if params[:realm_id]
+ @realms ||= driver.realms(credentials)
+ end
+
+ collection :instances do
+
+ standard_show_operation
+ standard_index_operation
+
+ operation :create, :with_capability => :create_instance do
+ param :image_id, :string, :required
+ param :realm_id, :string, :optional
+ param :hwp_id, :string, :optional
+ control do
+ @instance = driver.create_instance(credentials, params[:image_id], params)
+ if @instance.kind_of? Array
+ @elements = @instance
+ action_handler = "index"
+ else
+ response['Location'] = instance_url(@instance.id)
+ action_handler = "show"
+ end
+ status 201 # Created
+ respond_to do |format|
+ format.xml { haml :"instances/#{action_handler}" }
+ format.json { xml_to_json("instances/#{action_handler}") }
+ format.html do
+ if @elements
+ haml :"instances/index"
+ elsif @instance and @instance.id
+ response['Location'] = instance_url(@instance.id)
+ haml :"instances/show"
+ else
+ redirect instances_url
+ end
+ end
+ end
+ end
+ end
+
+ action :reboot, :with_capability => :reboot_instance do
+ description "Reboot a running instance."
+ control { instance_action(:reboot) }
+ end
+
+ action :start, :with_capability => :start_instance do
+ description "Start an instance."
+ control { instance_action(:start) }
+ end
+
+ action :stop, :with_capability => :stop_instance do
+ description "Stop a running instance."
+ control { instance_action(:stop) }
+ end
+
+ operation :destroy, :with_capability => :destroy_instance do
+ control { instance_action(:destroy) }
+ end
+
+ action :run, :with_capability => :run_instance do
+ param :id, :string, :required
+ param :cmd, :string, :required, [], "Shell command to run on instance"
+ param :private_key, :string, :optional, [], "Private key in PEM format for authentication"
+ param :password, :string, :optional, [], "Password used for authentication"
+ control do
+ @output = driver.run_on_instance(credentials, params)
+ respond_to do |format|
+ format.xml { haml :"instances/run" }
+ format.html { haml :"instances/run" }
+ end
+ end
+ end
+ end
+
+ end
+end
diff --git a/server/lib/deltacloud/collections/keys.rb b/server/lib/deltacloud/collections/keys.rb
new file mode 100644
index 0000000..55d0fa8
--- /dev/null
+++ b/server/lib/deltacloud/collections/keys.rb
@@ -0,0 +1,61 @@
+# 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::Collections
+ class Keys < Base
+ check_capability :for => lambda { |m| driver.respond_to? m }
+ check_features :for => lambda { |c, f| driver.class.has_feature?(c, f) }
+
+ get route_for('/keys/new') do
+ respond_to do |format|
+ format.html { haml :"keys/new" }
+ end
+ end
+
+ collection :keys do
+
+ standard_show_operation
+ standard_index_operation
+
+ operation :create, :with_capability => :create_key do
+ param :name, :string, :required
+ control do
+ @key = driver.create_key(credentials, { :key_name => params[:name] })
+ status 201
+ response['Location'] = key_url(@key.id)
+ respond_to do |format|
+ format.xml { haml :"keys/show", :ugly => true }
+ format.html { haml :"keys/show" }
+ format.json { convert_to_json(:key, @key)}
+ end
+ end
+ end
+
+ operation :destroy, :with_capability => :destroy_key do
+ control do
+ driver.destroy_key(credentials, { :id => params[:id]})
+ status 204
+ respond_to do |format|
+ format.xml
+ format.json
+ format.html { redirect(keys_url) }
+ end
+ end
+ end
+
+ end
+
+ end
+end
diff --git a/server/lib/deltacloud/collections/load_balancers.rb b/server/lib/deltacloud/collections/load_balancers.rb
new file mode 100644
index 0000000..d093ead
--- /dev/null
+++ b/server/lib/deltacloud/collections/load_balancers.rb
@@ -0,0 +1,85 @@
+# 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::Collections
+ class LoadBalancers < Base
+ check_capability :for => lambda { |m| driver.has_capability? m }
+
+ collection :load_balancers do
+ description "Load balancers are used distribute workload across multiple instances"
+
+ standard_index_operation
+ standard_show_operation
+
+ operation :create do
+ param :name, :string, :required
+ param :realm_id, :string, :required
+ param :listener_protocol, :string, :required, ['HTTP', 'TCP']
+ param :listener_balancer_port, :string, :required
+ param :listener_instance_port, :string, :required
+ control do
+ @load_balancer = driver.create_load_balancer(credentials, params)
+ status 201 # Created
+ response['Location'] = load_balancer_url(@instance.id)
+ respond_to do |format|
+ format.xml { haml :"load_balancers/show" }
+ format.json { convert_to_json(:load_balancer, @load_balancer) }
+ format.html { haml :"load_balancers/show" }
+ end
+ end
+ end
+
+ action :register do
+ param :instance_id, :string, :required
+ control do
+ driver.lb_register_instance(credentials, params)
+ @load_balancer = driver.load_balancer(credential, params[:id])
+ respond_to do |format|
+ format.xml { haml :'load_balancers/show' }
+ format.json ( xml_to_json('load_balancers/show'))
+ format.html { haml :'load_balancers/show' }
+ end
+ end
+ end
+
+ action :unregister do
+ param :instance_id, :string, :required
+ control do
+ driver.lb_unregister_instance(credentials, params)
+ @load_balancer = driver.load_balancer(credential, params[:id])
+ respond_to do |format|
+ format.xml { haml :'load_balancers/show' }
+ format.json ( xml_to_json('load_balancers/show'))
+ format.html { haml :'load_balancers/show' }
+ end
+ end
+ end
+
+ operation :destroy do
+ control do
+ driver.destroy_load_balancer(credentials, params[:id])
+ status 204
+ respond_to do |format|
+ format.xml
+ format.json
+ format.html { redirect(load_balancers_url) }
+ end
+ end
+ end
+
+ end
+
+ end
+end
diff --git a/server/lib/deltacloud/collections/realms.rb b/server/lib/deltacloud/collections/realms.rb
new file mode 100644
index 0000000..3f21625
--- /dev/null
+++ b/server/lib/deltacloud/collections/realms.rb
@@ -0,0 +1,27 @@
+# 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::Collections
+ class Realms < Base
+
+ collection :realms do
+ description "Within a cloud provider a realm represents a boundary containing resources"
+
+ standard_index_operation
+ standard_show_operation
+
+ end
+ end
+end
diff --git a/server/lib/deltacloud/collections/storage_snapshots.rb b/server/lib/deltacloud/collections/storage_snapshots.rb
new file mode 100644
index 0000000..b468614
--- /dev/null
+++ b/server/lib/deltacloud/collections/storage_snapshots.rb
@@ -0,0 +1,51 @@
+# 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::Collections
+ class StorageSnapshots < Base
+ check_capability :for => lambda { |m| driver.respond_to? m }
+ check_features :for => lambda { |c, f| driver.class.has_feature?(c, f) }
+
+ new_route_for(:storage_snapshots)
+
+ collection :storage_snapshots do
+ standard_index_operation
+ standard_show_operation
+
+ operation :create, :with_capability => :create_storage_snapshot do
+ param :volume_id, :string, :required
+ control do
+ @storage_snapshot = driver.create_storage_snapshot(credentials, params)
+ status 201 # Created
+ response['Location'] = storage_snapshot_url(@storage_snapshot.id)
+ show(:storage_snapshot)
+ end
+ end
+
+ operation :destroy, :with_capability => :destroy_storage_snapshot do
+ control do
+ driver.destroy_storage_snapshot(credentials, params)
+ status 204
+ respond_to do |format|
+ format.xml
+ format.json
+ format.html { redirect(storage_snapshots_url) }
+ end
+ end
+ end
+ end
+
+ end
+end
diff --git a/server/lib/deltacloud/collections/storage_volumes.rb b/server/lib/deltacloud/collections/storage_volumes.rb
new file mode 100644
index 0000000..9cdcd66
--- /dev/null
+++ b/server/lib/deltacloud/collections/storage_volumes.rb
@@ -0,0 +1,99 @@
+# 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::Collections
+ class StorageVolumes < Base
+
+ check_capability :for => lambda { |m| driver.respond_to? m }
+ check_features :for => lambda { |c, f| driver.class.has_feature?(c, f) }
+
+ new_route_for(:storage_volumes)
+
+ get route_for("/storage_volumes/:id/attach_instance") do
+ @instances = driver.instances(credentials)
+ respond_to do |format|
+ format.html{ haml :"storage_volumes/attach"}
+ end
+ end
+
+ collection :storage_volumes do
+
+ standard_index_operation
+ standard_show_operation
+
+ operation :show, :with_capability => :storage_volume do
+ control { show(:storage_volume) }
+ end
+
+ operation :create do
+ param :snapshot_id, :string, :optional
+ param :capacity, :string, :optional
+ param :realm_id, :string, :optional
+ control do
+ @storage_volume = driver.create_storage_volume(credentials, params)
+ status 201
+ response['Location'] = storage_volume_url(@storage_volume.id)
+ respond_to do |format|
+ format.xml { haml :"storage_volumes/show" }
+ format.html { haml :"storage_volumes/show" }
+ format.json { convert_to_json(:storage_volume, @storage_volume) }
+ end
+ end
+ end
+
+ action :attach, :with_capability => :attach_storage_volume do
+ param :instance_id,:string, :required
+ param :device, :string, :required
+ control do
+ @storage_volume = driver.attach_storage_volume(credentials, params)
+ status 202
+ respond_to do |format|
+ format.html { redirect(storage_volume_url(params[:id]))}
+ format.xml { haml :"storage_volumes/show" }
+ format.json { convert_to_json(:storage_volume, @storage_volume) }
+ end
+ end
+ end
+
+ action :detach, :with_capability => :detach_storage_volume do
+ control do
+ volume = driver.storage_volume(credentials, :id => params[:id])
+ @storage_volume = driver.detach_storage_volume(credentials, :id => volume.id,
+ :instance_id => volume.instance_id,
+ :device => volume.device)
+ status 202
+ respond_to do |format|
+ format.html { redirect(storage_volume_url(params[:id]))}
+ format.xml { haml :"storage_volumes/show" }
+ format.json { convert_to_json(:storage_volume, @storage_volume) }
+ end
+ end
+ end
+
+ operation :destroy, :with_capability => :destroy_storage_volume do
+ control do
+ driver.destroy_storage_volume(credentials, params)
+ status 204
+ respond_to do |format|
+ format.xml
+ format.json
+ format.html { redirect(storage_volumes_url) }
+ end
+ end
+ end
+
+ end
+ end
+end
diff --git a/server/lib/deltacloud/drivers/base_driver.rb b/server/lib/deltacloud/drivers/base_driver.rb
new file mode 100644
index 0000000..5fb1a79
--- /dev/null
+++ b/server/lib/deltacloud/drivers/base_driver.rb
@@ -0,0 +1,265 @@
+#
+# 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
+
+ class BaseDriver
+
+ include ExceptionHandler
+
+ def self.driver_name
+ name.split('::').last.gsub('Driver', '').downcase
+ end
+
+ def self.features
+ @features ||= []
+ end
+
+ def self.features_for(entity)
+ features.inject([]) do |result, item|
+ result << item[entity] if item.has_key? entity
+ result
+ end
+ end
+
+ def self.feature(collection, feature_name)
+ return if has_feature?(collection, feature_name)
+ features << { collection => feature_name }
+ end
+
+ def self.has_feature?(collection, feature_name)
+ features.any? { |f| (f.values.first == feature_name) && (f.keys.first == collection) }
+ end
+
+ def name
+ self.class.name.split('::').last.gsub('Driver', '').downcase
+ end
+
+ def self.exceptions(&block)
+ ExceptionHandler::exceptions(&block)
+ end
+
+ def self.define_hardware_profile(name,&block)
+ @hardware_profiles ||= []
+ hw_profile = @hardware_profiles.find{|e| e.name == name}
+ return if hw_profile
+ hw_profile = ::Deltacloud::HardwareProfile.new( name, &block )
+ @hardware_profiles << hw_profile
+ hw_params = hw_profile.params
+ # FIXME: Features
+ #unless hw_params.empty?
+ # feature :instances, :hardware_profiles do
+ # decl.operation(:create) { add_params(hw_params) }
+ # end
+ #end
+ end
+
+ def self.hardware_profiles
+ @hardware_profiles ||= []
+ @hardware_profiles
+ end
+
+ def hardware_profiles(credentials, opts = nil)
+ results = self.class.hardware_profiles
+ filter_hardware_profiles(results, opts)
+ end
+
+ def hardware_profile(credentials, name)
+ name = name[:id] if name.kind_of? Hash
+ hardware_profiles(credentials, :id => name).first
+ end
+
+ def filter_hardware_profiles(profiles, opts)
+ if opts
+ if v = opts[:architecture]
+ profiles = profiles.select { |hwp| hwp.include?(:architecture, v) }
+ end
+ # As a request param, we call 'name' 'id'
+ if v = opts[:id]
+ profiles = profiles.select { |hwp| hwp.name == v }
+ end
+ end
+ profiles
+ end
+
+ def find_hardware_profile(credentials, name, image_id)
+ hwp = nil
+ if name
+ unless hwp = hardware_profiles(credentials, :id => name).first
+ raise BackendError.new(400, "bad-hardware-profile-name",
+ "Hardware profile '#{name}' does not exist", nil)
+ end
+ else
+ unless image = image(credentials, :id=>image_id)
+ raise BackendError.new(400, "bad-image-id",
+ "Image with ID '#{image_id}' does not exist", nil)
+ end
+ hwp = hardware_profiles(credentials,
+ :architecture=>image.architecture).first
+ end
+ return hwp
+ end
+
+ def self.define_instance_states(&block)
+ machine = ::Deltacloud::StateMachine.new(&block)
+ @instance_state_machine = machine
+ end
+
+ def self.instance_state_machine
+ @instance_state_machine
+ end
+
+ def instance_state_machine
+ self.class.instance_state_machine
+ end
+
+ def instance_actions_for(state)
+ actions = []
+ state_key = state.downcase.to_sym
+ states = instance_state_machine.states()
+ current_state = states.find{|e| e.name == state.underscore.to_sym }
+ if ( current_state )
+ actions = current_state.transitions.collect{|e|e.action}
+ actions.reject!{|e| e.nil?}
+ end
+ actions
+ end
+
+ def has_capability?(method)
+ (self.class.instance_methods - self.class.superclass.methods).include? method
+ end
+
+ ## Capabilities
+ # The rabbit dsl supports declaring a capability that is required
+ # in the backend driver for the call to succeed. A driver can
+ # provide a capability by implementing the method with the same
+ # name as the capability. Below is a list of the capabilities as
+ # the expected method signatures.
+ #
+ # Following the capability list are the resource member show
+ # methods. They each require that the corresponding collection
+ # method be defined
+ #
+ # TODO: standardize all of these to the same signature (credentials, opts)
+ #
+ # def realms(credentials, opts=nil)
+ #
+ # def images(credentials, ops)
+ #
+ # def instances(credentials, ops)
+ # def create_instance(credentials, image_id, opts)
+ # def start_instance(credentials, id)
+ # def stop_instance(credentials, id)
+ # def reboot_instance(credentials, id)
+ #
+ # def storage_volumes(credentials, ops)
+ #
+ # def storage_snapshots(credentials, ops)
+ #
+ # def buckets(credentials, opts = nil)
+ # def create_bucket(credentials, name, opts=nil)
+ # def delete_bucket(credentials, name, opts=nil)
+ #
+ # def blobs(credentials, opts = nil)
+ # def blob_data(credentials, bucket_id, blob_id, opts)
+ # def create_blob(credentials, bucket_id, blob_id, blob_data, opts=nil)
+ # def delete_blob(credentials, bucket_id, blob_id, opts=nil)
+ #
+ # def keys(credentials, opts)
+ # def create_key(credentials, opts)
+ # def destroy_key(credentials, opts)
+ #
+ # def firewalls(credentials, opts)
+ # def create_firewall(credentials, opts)
+ # def delete_firewall(credentials, opts)
+ # def create_firewall_rule(credentials, opts)
+ # def delete_firewall_rule(credentials, opts)
+ # def providers(credentials)
+ def realm(credentials, opts)
+ realms = realms(credentials, opts).first if has_capability?(:realms)
+ end
+
+ def image(credentials, opts)
+ images(credentials, opts).first if has_capability?(:images)
+ end
+
+ def instance(credentials, opts)
+ instances(credentials, opts).first if has_capability?(:instances)
+ end
+
+ def storage_volume(credentials, opts)
+ storage_volumes(credentials, opts).first if has_capability?(:storage_volumes)
+ end
+
+ def storage_snapshot(credentials, opts)
+ storage_snapshots(credentials, opts).first if has_capability?(:storage_snapshots)
+ end
+
+ def bucket(credentials, opts = {})
+ #list of objects within bucket
+ buckets(credentials, opts).first if has_capability?(:buckets)
+ end
+
+ def blob(credentials, opts = {})
+ blobs(credentials, opts).first if has_capability?(:blobs)
+ end
+
+ def key(credentials, opts=nil)
+ keys(credentials, opts).first if has_capability?(:keys)
+ end
+
+ def firewall(credentials, opts={})
+ firewalls(credentials, opts).first if has_capability?(:firewalls)
+ end
+
+ MEMBER_SHOW_METHODS =
+ [ :realm, :image, :instance, :storage_volume, :bucket, :blob, :key, :firewall ]
+
+ def filter_on(collection, attribute, opts)
+ return collection if opts.nil?
+ return collection if opts[attribute].nil?
+ filter = opts[attribute]
+ if ( filter.is_a?( Array ) )
+ return collection.select{|e| filter.include?( e.send(attribute) ) }
+ else
+ return collection.select{|e| filter == e.send(attribute) }
+ end
+ end
+
+ def supported_collections
+ DEFAULT_COLLECTIONS
+ end
+
+ def has_collection?(collection)
+ supported_collections.include?(collection)
+ end
+
+ def catched_exceptions_list
+ { :error => [], :auth => [], :glob => [] }
+ end
+
+ def api_provider
+ Thread.current[:provider] || ENV['API_PROVIDER']
+ end
+
+ # Return an array of the providers statically configured
+ # in the driver's YAML file
+ def configured_providers
+ []
+ end
+ end
+
+end
diff --git a/server/lib/deltacloud/drivers/exceptions.rb b/server/lib/deltacloud/drivers/exceptions.rb
new file mode 100644
index 0000000..a89b05f
--- /dev/null
+++ b/server/lib/deltacloud/drivers/exceptions.rb
@@ -0,0 +1,191 @@
+# 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
+ module ExceptionHandler
+
+ class DeltacloudException < StandardError
+
+ attr_accessor :code, :name, :message, :backtrace, :request
+
+ def initialize(code, name, message, backtrace, request=nil)
+ @code, @name, @message = code, name, message
+ @backtrace = backtrace
+ @request = request
+ self
+ end
+
+ end
+
+ class AuthenticationFailure < DeltacloudException
+ def initialize(e, message=nil)
+ message ||= e.message
+ super(401, e.class.name, message, e.backtrace)
+ end
+ end
+
+ class UnknownMediaTypeError < DeltacloudException
+ def initialize(e, message=nil)
+ message ||= e.message
+ super(406, e.class.name, message, e.backtrace)
+ end
+ end
+
+ class MethodNotAllowed < DeltacloudException
+ def initialize(e, message=nil)
+ message ||= e.message
+ super(405, e.class.name, message, e.backtrace)
+ end
+ end
+
+ class ValidationFailure < DeltacloudException
+ def initialize(e, message=nil)
+ message ||= e.message
+ super(400, e.class.name, message, e.backtrace)
+ end
+ end
+
+ class BackendError < DeltacloudException
+ def initialize(e, message=nil)
+ message ||= e.message
+ super(500, e.class.name, message, e.backtrace, message)
+ end
+ end
+
+ class ProviderError < DeltacloudException
+ def initialize(e, message)
+ message ||= e.message
+ super(502, e.class.name, message, e.backtrace)
+ end
+ end
+
+ class ProviderTimeout < DeltacloudException
+ def initialize(e, message)
+ message ||= e.message
+ super(504, e.class.name, message, e.backtrace)
+ end
+ end
+
+ class NotImplemented < DeltacloudException
+ def initialize(e, message)
+ message ||= e.message
+ super(501, e.class.name, message, e.backtrace)
+ end
+ end
+
+ class ObjectNotFound < DeltacloudException
+ def initialize(e, message)
+ message ||= e.message
+ super(404, e.class.name, message, e.backtrace)
+ end
+ end
+
+ class NotSupported < DeltacloudException
+ def initialize(message)
+ super(501, self.class.name, message, self.backtrace)
+ end
+ end
+
+ class ExceptionDef
+ attr_accessor :status
+ attr_accessor :message
+ attr_reader :conditions
+ attr_reader :handler
+
+ def initialize(conditions, &block)
+ @conditions = conditions
+ instance_eval(&block) if block_given?
+ end
+
+ def status(code)
+ self.status = code
+ end
+
+ def message(message)
+ self.message = message
+ end
+
+ def exception(handler)
+ self.handler = handler
+ end
+
+ # Condition can be class or regexp
+ #
+ def match?(e)
+ @conditions.each do |c|
+ return true if c.class == Class && e.class == c
+ return true if c.class == Regexp && (e.class.name =~ c or e.message =~ c)
+ end
+ return false
+ end
+
+ def handler(e)
+ return @handler if @handler
+ case @status
+ when 401 then Deltacloud::ExceptionHandler::AuthenticationFailure.new(e, @message)
+ when 404 then Deltacloud::ExceptionHandler::ObjectNotFound.new(e, @message)
+ when 406 then Deltacloud::ExceptionHandler::UnknownMediaTypeError.new(e, @message)
+ when 405 then Deltacloud::ExceptionHandler::MethodNotAllowed.new(e, @message)
+ when 400 then Deltacloud::ExceptionHandler::ValidationFailure.new(e, @message)
+ when 500 then Deltacloud::ExceptionHandler::BackendError.new(e, @message)
+ when 501 then Deltacloud::ExceptionHandler::NotImplemented.new(e, @message)
+ when 502 then Deltacloud::ExceptionHandler::ProviderError.new(e, @message)
+ when 504 then Deltacloud::ExceptionHandler::ProviderTimeout.new(e, @message)
+ end
+ end
+
+ end
+
+ class Exceptions
+ attr_reader :exception_definitions
+
+ def initialize(&block)
+ @exception_definitions = []
+ instance_eval(&block) if block_given?
+ self
+ end
+
+ def on(*conditions, &block)
+ @exception_definitions << ExceptionDef::new(conditions, &block) if block_given?
+ end
+ end
+
+ def self.exceptions(&block)
+ @definitions = Exceptions.new(&block).exception_definitions if block_given?
+ @definitions
+ end
+
+ def safely(&block)
+ begin
+ block.call
+ rescue
+ report_method = $stderr.respond_to?(:err) ? :err : :puts
+ Deltacloud::ExceptionHandler::exceptions.each do |exdef|
+ if exdef.match?($!)
+ new_exception = exdef.handler($!)
+ m = new_exception.message.nil? ? $!.message : new_exception.message
+ $stderr.send(report_method, "#{[$!.class.to_s, m].join(':')}\n#{$!.backtrace[0..10].join("\n")}")
+ raise exdef.handler($!) unless new_exception.nil?
+ end
+ end
+ $stderr.send(report_method, "[NO HANDLED] #{[$!.class.to_s, $!.message].join(': ')}\n#{$!.backtrace.join("\n")}")
+ raise Deltacloud::ExceptionHandler::BackendError.new($!, "Unhandled exception or status code (#{$!.message})")
+ end
+ end
+
+ end
+
+end
diff --git a/server/lib/deltacloud/helpers.rb b/server/lib/deltacloud/helpers.rb
index cf8531a..73f79ec 100644
--- a/server/lib/deltacloud/helpers.rb
+++ b/server/lib/deltacloud/helpers.rb
@@ -14,10 +14,84 @@
# License for the specific language governing permissions and limitations
# under the License.
-require 'deltacloud/helpers/application_helper'
-require 'deltacloud/helpers/json_helper'
-require 'deltacloud/helpers/conversion_helper'
-require 'deltacloud/helpers/hardware_profiles_helper'
-require 'deltacloud/helpers/blob_stream'
+require_relative 'helpers/driver_helper'
+require_relative 'helpers/auth_helper'
+require_relative 'helpers/url_helper'
+require_relative 'helpers/assets_helper'
+require_relative 'helpers/deltacloud_helper'
+require_relative 'helpers/rabbit_helper'
+require_relative 'helpers/blob_stream_helper'
+require_relative 'core_ext/string'
+require_relative 'core_ext/array'
+require_relative 'core_ext/hash'
+require_relative 'core_ext/integer'
+require_relative 'core_ext/proc'
-helpers ApplicationHelper, ConversionHelper, HardwareProfilesHelper, JSONHelper
+module Deltacloud::Collections
+ class Base < Sinatra::Base
+
+ extend Deltacloud::Helpers::Drivers
+ include Sinatra::Rabbit::Features
+
+ helpers Deltacloud::Helpers::Drivers
+ helpers Sinatra::AuthHelper
+ helpers Sinatra::UrlForHelper
+ helpers Sinatra::StaticAssets::Helpers
+ helpers Rack::RespondTo::Helpers
+ helpers Deltacloud::Helpers::Application
+
+ register Rack::RespondTo
+
+ enable :xhtml
+ enable :dump_errors
+ enable :show_errors
+ enable :method_override
+ disable :show_exceptions
+
+ set :root_url, API_ROOT_URL
+ set :version, API_VERSION
+ set :root, File.join(File.dirname(__FILE__), '..', '..')
+ set :views, root + '/views'
+ set :public_folder, root + '/public'
+
+ error do
+ report_error
+ end
+
+ error Deltacloud::ExceptionHandler::ValidationFailure do
+ report_error
+ end
+
+ 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
+ end
+
+ after do
+ headers 'Server' => 'Apache-Deltacloud/' + settings.version
+ end
+
+ def self.new_route_for(route, &block)
+ get route_for('/' + route.to_s + '/new') do
+ instance_eval(&block) if block_given?
+ respond_to do |format|
+ format.html do
+ haml :"#{route}/new"
+ end
+ end
+ end
+ end
+
+ def self.check_capability(opts={})
+ Sinatra::Rabbit.set :check_capability, opts[:for]
+ end
+
+ def self.check_features(opts={})
+ Sinatra::Rabbit.set :check_features, opts[:for]
+ end
+
+ def self.route_for(url)
+ "#{settings.root_url}#{url}"
+ end
+ end
+end
diff --git a/server/lib/deltacloud/models.rb b/server/lib/deltacloud/models.rb
index af02520..099afda 100644
--- a/server/lib/deltacloud/models.rb
+++ b/server/lib/deltacloud/models.rb
@@ -1,4 +1,3 @@
-#
# 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
@@ -14,20 +13,11 @@
# License for the specific language governing permissions and limitations
# under the License.
-require 'deltacloud/models/base_model'
-require 'deltacloud/models/realm'
-require 'deltacloud/models/image'
-require 'deltacloud/models/instance'
-require 'deltacloud/models/key'
-require 'deltacloud/models/address'
-require 'deltacloud/models/instance_address'
-require 'deltacloud/models/instance_profile'
-require 'deltacloud/models/storage_snapshot'
-require 'deltacloud/models/storage_volume'
-require 'deltacloud/models/bucket'
-require 'deltacloud/models/blob'
-require 'deltacloud/models/load_balancer'
-require 'deltacloud/models/firewall'
-require 'deltacloud/models/firewall_rule'
-require 'deltacloud/models/provider'
-require 'deltacloud/models/metric'
+require_relative 'models/base_model'
+
+# Include all models
+
+Dir[File.join(File::dirname(__FILE__), "models", "*.rb")].each do |model|
+ next if model =~ /base_model\.rb$/
+ require model
+end
diff --git a/server/lib/deltacloud/models/hardware_profile.rb b/server/lib/deltacloud/models/hardware_profile.rb
new file mode 100644
index 0000000..45e77a1
--- /dev/null
+++ b/server/lib/deltacloud/models/hardware_profile.rb
@@ -0,0 +1,194 @@
+#
+# 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
+ class HardwareProfile
+
+ UNITS = {
+ :memory => "MB",
+ :storage => "GB",
+ :architecture => "label",
+ :cpu => "count"
+ }
+
+ def self.unit(name)
+ UNITS[name]
+ end
+
+ class Property
+ attr_reader :name, :kind, :default
+ # kind == :range
+ attr_reader :first, :last
+ # kind == :enum
+ attr_reader :values
+ # kind == :fixed
+ attr_reader :value
+
+ def initialize(name, values, opts = {})
+ @name = name
+ if values.is_a?(Range)
+ @kind = :range
+ @first = values.first
+ @last = values.last
+ @default = values.first
+ elsif values.is_a?(Array)
+ @kind = :enum
+ @values = values
+ @default = values.first
+ else
+ @kind = :fixed
+ @value = values
+ @default = @value
+ end
+ @default = opts[:default] if opts[:default]
+ end
+
+ def unit
+ HardwareProfile.unit(name)
+ end
+
+ def param
+ :"hwp_#{name}"
+ end
+
+ def fixed?
+ kind == :fixed
+ end
+
+ def valid?(v)
+ v = convert_property_value_type(v)
+ case kind
+ # NOTE:
+ # Currently we cannot validate fixed values because of UI
+ # limitation. In UI we have multiple hwp_* properties which overide
+ # each other.
+ # Then provider have one 'static' hardware profile and one
+ # 'customizable' when user select the static one the UI also send
+ # values from the customizable one (which will lead to a validation
+ # error because validation algorith will think that client want to
+ # overide fixed values.
+ #
+ # when :fixed then (v == @default.to_s)
+ when :fixed then true
+ when :range then match_type?(first, v) and (first..last).include?(v)
+ when :enum then match_type?(values.first, v) and values.include?(v)
+ else false
+ end
+ end
+
+ def to_param
+ if defined? Sinatra::Rabbit
+ Sinatra::Rabbit::Param.new([param, :string, :optional, []])
+ end
+ end
+
+ def include?(v)
+ if kind == :fixed
+ return v == value
+ else
+ return values.include?(v)
+ end
+ end
+
+ private
+
+ def match_type?(reference, value)
+ true if reference.class == value.class
+ end
+
+ def convert_property_value_type(v)
+ return v.to_f if v =~ /(\d+)\.(\d+)/
+ return v.to_i if v =~ /(\d+)/
+ v.to_s
+ end
+ end
+
+ class << self
+ def property(prop)
+ define_method(prop) do |*args|
+ values, opts, *ignored = *args
+ instvar = :"@#{prop}"
+ unless values.nil?
+ @properties[prop] = Property.new(prop, values, opts || {})
+ end
+ @properties[prop]
+ end
+ end
+ end
+
+ attr_reader :name
+ property :cpu
+ property :architecture
+ property :memory
+ property :storage
+
+ def initialize(name,&block)
+ @properties = {}
+ @name = name
+ instance_eval &block if block_given?
+ end
+
+ def each_property(&block)
+ @properties.each_value { |prop| yield prop }
+ end
+
+ def properties
+ @properties.values
+ end
+
+ def property(name)
+ @properties[name.to_sym]
+ end
+
+ def default?(prop, v)
+ p = @properties[prop.to_sym]
+ p && p.default.to_s == v
+ end
+
+ def to_hash
+ props = []
+ self.each_property do |p|
+ if p.kind.eql? :fixed
+ props << { :kind => p.kind, :value => p.value, :name => p.name, :unit => p.unit }
+ else
+ param = { :operation => "create", :method => "post", :name => p.name }
+ if p.kind.eql? :range
+ param[:range] = { :first => p.first, :last => p.last }
+ elsif p.kind.eql? :enum
+ param[:enum] = p.values.collect { |v| { :entry => v } }
+ end
+ param
+ props << { :kind => p.kind, :value => p.default, :name => p.name, :unit => p.unit, :param => param }
+ end
+ end
+ {
+ :id => self.name,
+ :properties => props
+ }
+ end
+
+ def include?(prop, v)
+ p = @properties[prop]
+ p.nil? || p.include?(v)
+ end
+
+ def params
+ @properties.values.inject([]) { |m, prop|
+ m << prop.to_param
+ }.compact
+ end
+ end
+end
diff --git a/server/lib/deltacloud/models/state_machine.rb b/server/lib/deltacloud/models/state_machine.rb
new file mode 100644
index 0000000..19fb9f2
--- /dev/null
+++ b/server/lib/deltacloud/models/state_machine.rb
@@ -0,0 +1,99 @@
+#
+# 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
+ class StateMachine
+
+ attr_reader :states
+ def initialize(&block)
+ @states = []
+ instance_eval &block if block
+ end
+
+ def start()
+ state(:start)
+ end
+
+ def finish()
+ state(:finish)
+ end
+
+ def state(name)
+ state = @states.find{|e| e.name == name.to_sym}
+ if ( state.nil? )
+ state = State.new( self, name.to_sym )
+ @states << state
+ end
+ state
+ end
+
+ def method_missing(sym,*args)
+ return state( sym ) if ( args.empty? )
+ super( sym, *args )
+ end
+
+ class State
+
+ attr_reader :name
+ attr_reader :transitions
+
+ def initialize(machine, name)
+ @machine = machine
+ @name = name
+ @transitions = []
+ end
+
+ def to_s
+ self.name.to_s
+ end
+
+ def to(destination_name)
+ destination = @machine.state(destination_name)
+ transition = Transition.new( @machine, destination )
+ @transitions << transition
+ transition
+ end
+
+ end
+
+ class Transition
+
+ attr_reader :destination
+ attr_reader :action
+
+ def initialize(machine, destination)
+ @machine = machine
+ @destination = destination
+ @auto = false
+ @action = nil
+ end
+
+ def automatically
+ @auto = true
+ end
+
+ def automatically?
+ @auto
+ end
+
+ def on(action)
+ @action = action
+ end
+
+ end
+
+ end
+end
diff --git a/server/lib/deltacloud/state_machine.rb b/server/lib/deltacloud/state_machine.rb
deleted file mode 100644
index facd4ba..0000000
--- a/server/lib/deltacloud/state_machine.rb
+++ /dev/null
@@ -1,115 +0,0 @@
-#
-# 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
- class StateMachine
-
- attr_reader :states
- def initialize(opts = {}, &block)
- @all_states = opts[:all_states]
- @all_actions = opts[:all_actions]
- @states = []
- instance_eval &block if block
- end
-
- def start()
- state(:start)
- end
-
- def finish()
- state(:finish)
- end
-
- def state(name)
- unless valid_state_name?(name)
- raise "State '#{name}' not in list of allowed states"
- end
- state = @states.find{|e| e.name == name.to_sym}
- if ( state.nil? )
- state = State.new( self, name.to_sym )
- @states << state
- end
- state
- end
-
- def valid_state_name?(name)
- @all_states.nil? || @all_states.include?(name.to_sym)
- end
-
- def valid_action_name?(name)
- @all_actions.nil? || @all_actions.include?(name.to_sym)
- end
-
- def method_missing(sym,*args)
- return state( sym ) if ( args.empty? )
- super( sym, *args )
- end
-
- class State
-
- attr_reader :name
- attr_reader :transitions
-
- def initialize(machine, name)
- @machine = machine
- @name = name
- @transitions = []
- end
-
- def to_s
- self.name.to_s
- end
-
- def to(destination_name)
- destination = @machine.state(destination_name)
- transition = Transition.new( @machine, destination )
- @transitions << transition
- transition
- end
-
- end
-
- class Transition
-
- attr_reader :destination
- attr_reader :action
-
- def initialize(machine, destination)
- @machine = machine
- @destination = destination
- @auto = false
- @action = nil
- end
-
- def automatically
- @auto = true
- end
-
- def automatically?
- @auto
- end
-
- def on(action)
- unless @machine.valid_action_name?(action)
- raise "Action '#{action}' not in list of allowed actions"
- end
- @action = action
- end
-
- end
-
- end
-end
--
1.7.10.1