Return-Path: X-Original-To: apmail-deltacloud-dev-archive@www.apache.org Delivered-To: apmail-deltacloud-dev-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 7D754C83E for ; Mon, 21 May 2012 10:07:42 +0000 (UTC) Received: (qmail 78217 invoked by uid 500); 21 May 2012 10:07:42 -0000 Delivered-To: apmail-deltacloud-dev-archive@deltacloud.apache.org Received: (qmail 78198 invoked by uid 500); 21 May 2012 10:07:42 -0000 Mailing-List: contact dev-help@deltacloud.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@deltacloud.apache.org Delivered-To: mailing list dev@deltacloud.apache.org Received: (qmail 78186 invoked by uid 99); 21 May 2012 10:07:42 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 21 May 2012 10:07:41 +0000 X-ASF-Spam-Status: No, hits=-5.0 required=5.0 tests=RCVD_IN_DNSWL_HI,SPF_HELO_PASS,SPF_PASS,T_FRT_BELOW2 X-Spam-Check-By: apache.org Received-SPF: pass (nike.apache.org: domain of mfojtik@redhat.com designates 209.132.183.28 as permitted sender) Received: from [209.132.183.28] (HELO mx1.redhat.com) (209.132.183.28) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 21 May 2012 10:07:21 +0000 Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id q4LA70LF007747 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Mon, 21 May 2012 06:07:00 -0400 Received: from dhcp-29-121.brq.redhat.com (dhcp-29-121.brq.redhat.com [10.34.29.121]) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id q4LA6sJh020488 for ; Mon, 21 May 2012 06:06:59 -0400 From: mfojtik@redhat.com To: dev@deltacloud.apache.org Subject: [PATCH core 03/51] Core: Replaced obsolete or unused Deltacloud code Date: Mon, 21 May 2012 12:06:44 +0200 Message-Id: <1337594852-42550-4-git-send-email-mfojtik@redhat.com> In-Reply-To: <1337594852-42550-1-git-send-email-mfojtik@redhat.com> References: <1337594852-42550-1-git-send-email-mfojtik@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.22 From: Michal Fojtik Signed-off-by: Michal fojtik --- 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 | 62 --- 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(+), 2289 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) - "" - end - - def render_cdata(text) - "" - 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) + "" + end + + def render_cdata(text) + "" + 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 862d680..0000000 --- a/server/lib/deltacloud/helpers/hardware_profiles_helper.rb +++ /dev/null @@ -1,62 +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 - - #first by cpu - then by memory - def order_hardware_profiles(profiles) - #have to deal with opaque hardware profiles - uncomparables = profiles.select{|x| x.cpu.nil? or x.memory.nil? } - if uncomparables.empty? - profiles.sort_by{|a| [a.cpu.default, a.memory.default] } - else - (profiles - uncomparables).sort_by{|a| [a.cpu.default, a.memory.default] } + uncomparables - 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 + # :path_only, which will generate an absolute path within + # the current domain (the default), or :full, 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 and 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}" - 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 - # :path_only, which will generate an absolute path within - # the current domain (the default), or :full, 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