From: Michal Fojtik <[email protected]>
Signed-off-by: Michal fojtik <[email protected]> --- server/lib/deltacloud/helpers.rb | 86 ++- server/lib/deltacloud/server.rb | 1278 +------------------------------------- 2 files changed, 107 insertions(+), 1257 deletions(-) diff --git a/server/lib/deltacloud/helpers.rb b/server/lib/deltacloud/helpers.rb index cf8531a..7be8faf 100644 --- a/server/lib/deltacloud/helpers.rb +++ b/server/lib/deltacloud/helpers.rb @@ -1,4 +1,3 @@ -# # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. The @@ -14,10 +13,83 @@ # License for the specific language governing permissions and limitations # under the License. -require 'deltacloud/helpers/application_helper' -require 'deltacloud/helpers/json_helper' -require 'deltacloud/helpers/conversion_helper' -require 'deltacloud/helpers/hardware_profiles_helper' -require 'deltacloud/helpers/blob_stream' +require_relative 'helpers/driver_helper' +require_relative 'helpers/auth_helper' +require_relative 'helpers/url_helper' +require_relative 'helpers/assets_helper' +require_relative 'helpers/deltacloud_helper' +require_relative 'helpers/rabbit_helper' +require_relative 'core_ext/string' +require_relative 'core_ext/array' +require_relative 'core_ext/hash' +require_relative 'core_ext/integer' +require_relative 'core_ext/proc' + +module Deltacloud::Collections + class Base < Sinatra::Base + + extend Deltacloud::Helpers::Drivers + include Sinatra::Rabbit::Features + + helpers Deltacloud::Helpers::Drivers + helpers Sinatra::AuthHelper + helpers Sinatra::UrlForHelper + helpers Sinatra::StaticAssets::Helpers + helpers Rack::RespondTo::Helpers + helpers Deltacloud::Helpers::Application + + register Rack::RespondTo + + enable :xhtml + enable :dump_errors + enable :show_errors + disable :show_exceptions + + set :root_url, API_ROOT_URL + set :version, API_VERSION + set :root, File.join(File.dirname(__FILE__), '..', '..') + set :views, root + '/views' + set :public_folder, root + '/public' + + error do + report_error + end + + error Deltacloud::ExceptionHandler::ValidationFailure do + report_error + end + + before do + # Respond with 400, If we don't get a http Host header, + halt 400, "Unable to find HTTP Host header" if @env['HTTP_HOST'] == nil + end + + after do + headers 'Server' => 'Apache-Deltacloud/' + settings.version + end + + def self.new_route_for(route, &block) + get route_for('/' + route.to_s + '/new') do + instance_eval(&block) if block_given? + respond_to do |format| + format.html do + haml :"#{route}/new" + end + end + end + end + + def self.check_capability(opts={}) + Sinatra::Rabbit.set :check_capability, opts[:for] + end + + def self.check_features(opts={}) + Sinatra::Rabbit.set :check_features, opts[:for] + end + + def self.route_for(url) + "#{settings.root_url}#{url}" + end -helpers ApplicationHelper, ConversionHelper, HardwareProfilesHelper, JSONHelper + end +end diff --git a/server/lib/deltacloud/server.rb b/server/lib/deltacloud/server.rb index 7b499d9..ebb7cb7 100644 --- a/server/lib/deltacloud/server.rb +++ b/server/lib/deltacloud/server.rb @@ -1,1266 +1,44 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. The -# ASF licenses this file to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance with the -# License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -require 'sinatra' -require 'deltacloud' +require 'rubygems' +require 'crack' require 'json' -require 'sinatra/rack_accept' -require 'sinatra/static_assets' -require 'sinatra/rabbit' -require 'sinatra/lazy_auth' -require 'erb' +require 'yaml' require 'haml' -require 'open3' -require 'sinatra/sinatra_verbose' -require 'sinatra/rack_driver_select' -require 'sinatra/rack_runtime' -require 'sinatra/rack_etag' -require 'sinatra/rack_date' -require 'sinatra/rack_matrix_params' -require 'sinatra/rack_syslog' - -set :version, '0.5.0' - -include Deltacloud::Drivers -set :drivers, Proc.new { driver_config } - -Sinatra::Application.register Rack::RespondTo - -use Rack::ETag -use Rack::Runtime -use Rack::MatrixParams -use Rack::DriverSelect -use Rack::MediaType -use Rack::Date -use Rack::CommonLogger - -configure do - set :root_url, "/api" - set :views, File::join($top_srcdir, 'views') - # NOTE: Change :public to :public_folder once we update sinatra to 1.3 - # set :public_folder, File::join($top_srcdir, 'public') - if settings.respond_to? :public_folder - set :public_folder, File::join($top_srcdir, 'public') - else - set :public, File::join($top_srcdir, 'public') - end - # Try to load the driver on startup to fail early if there are issues - driver -end - -configure :production do - use Rack::SyslogLogger - set :logger, SyslogFile.new - disable :logging - enable :show_errors - enable :dump_errors -end - -configure :development do - # So we can just use puts for logging - set :raise_errors => false - set :show_exceptions, false - $stdout.sync = true - $stderr.sync = true -end - -# You could use $API_HOST environment variable to change your hostname to -# whatever you want (eg. if you running API behind NAT) -HOSTNAME=ENV['API_HOST'] ? ENV['API_HOST'] : nil - -error do - report_error -end - -error Deltacloud::ExceptionHandler::ValidationFailure do - report_error -end - -before do - # Respond with 400, If we don't get a http Host header, - halt 400, "Unable to find HTTP Host header" if @env['HTTP_HOST'] == nil -end - -after do - headers 'Server' => 'Apache-Deltacloud/' + settings.version -end - -# Redirect to /api -get '/' do redirect settings.root_url, 301; end - - -# Generate a root route for API docs -get "#{settings.root_url}/docs\/?" do - respond_to do |format| - format.html { haml :'docs/index' } - format.xml { haml :'docs/index' } - end -end - -get "#{settings.root_url}\/?" do - if params[:force_auth] - return [401, 'Authentication failed'] unless driver.valid_credentials?(credentials) - end - @collections = [:drivers] + driver.supported_collections - @driver_name = driver.name unless driver.name.to_sym == DRIVER - @providers = driver.configured_providers - respond_to do |format| - format.xml { haml :"api/show" } - format.json do - { :api => { - :version => settings.version, - :driver => driver_symbol, - :links => entry_points.collect do |l| - { :rel => l[0], :href => l[1] }.merge(json_features_for_entrypoint(l)) - end - } - }.to_json - end - format.html { haml :"api/show" } - end -end - -post "#{settings.root_url}\/?" do - p = {} - ["provider", "driver"].each { |k| p[k] = params[k] if params[k] } - p.delete("provider") if p["provider"] == "default" - q = p.map { |k,v| "#{k}=#{v}" }.join(";") - q = ";" + q unless q.empty? - redirect "#{settings.root_url}#{q}", 301 -end - -# Rabbit DSL - -collection :drivers do - global! - - description <<EOS -List all the drivers supported by this server. -EOS - - operation :index do - description "List all drivers" - control do - @drivers = settings.drivers - respond_to do |format| - format.xml { haml :"drivers/index" } - format.json { @drivers.to_json } - format.html { haml :"drivers/index" } - end - end - end - - operation :show do - description "Show details for a driver" - param :id, :string - control do - @name = params[:id].to_sym - if driver_symbol == @name - @providers = driver.providers(credentials) if driver.respond_to? :providers - end - @driver = settings.drivers[@name] - halt 404 unless @driver - respond_to do |format| - format.xml { haml :"drivers/show" } - format.json { @driver.to_json } - format.html { haml :"drivers/show" } - end - end - end -end - -collection :realms do - description <<END - Within a cloud provider a realm represents a boundary containing resources. - The exact definition of a realm is left to the cloud provider. - In some cases, a realm may represent different datacenters, different continents, - or different pools of resources within a single datacenter. - A cloud provider may insist that resources must all exist within a single realm in - order to cooperate. For instance, storage volumes may only be allowed to be mounted to - instances within the same realm. -END - - operation :index do - description <<END - Operation will list all available realms. Realms can be filtered using - the "architecture" parameter. -END - with_capability :realms - param :id, :string - param :architecture, :string, :optional, [ 'i386', 'x86_64' ] - control { filter_all(:realms) } - end - - #FIXME: It always shows whole list - operation :show do - description 'Show an realm identified by "id" parameter.' - with_capability :realm - param :id, :string, :required - control { show(:realm) } - end - -end - -collection :images do - description <<END - An image is a platonic form of a machine. Images are not directly executable, - but are a template for creating actual instances of machines. -END - - operation :new do - description "Form to create a new image resource" - param :instance_id, :string, "An instance from which the new image will be created from" - control do - @instance = Instance.new( :id => params[:instance_id] ) - respond_to do |format| - format.html { haml :"images/new" } - end - end - end - - operation :index do - description <<END - The images collection will return a set of all images - available to the current use. Images can be filtered using the - "owner_id" and "architecture" parameters. -END - with_capability :images - param :id, :string - param :architecture, :string, :optional - control { filter_all(:images) } - end - - operation :show do - description 'Show an image identified by "id" parameter.' - with_capability :image - param :id, :string, :required - control { show(:image) } - end - - operation :create do - description 'Create image from instance' - with_capability :create_image - param :instance_id, :string, :required - control do - @image = driver.create_image(credentials, { - :id => params[:instance_id], - :name => params[:name], - :description => params[:description] - }) - status 201 # Created - response['Location'] = image_url(@image.id) - respond_to do |format| - format.xml { haml :"images/show" } - format.json { convert_to_json(:image, @image) } - format.html { haml :"images/show" } - end - end - end - - operation :destroy do - description "Remove specified image from collection" - with_capability :destroy_image - param :id, :string, :required - control do - driver.destroy_image(credentials, params[:id]) - status 204 - respond_to do |format| - format.xml - format.json - format.html { redirect(images_url) } - end - end - end - -end - -collection :instance_states do - description "The possible states of an instance, and how to traverse between them " - - operation :index do - control do - @machine = driver.instance_state_machine - respond_to do |format| - format.xml { haml :'instance_states/show', :layout => false } - format.json do - out = [] - @machine.states.each do |state| - transitions = state.transitions.collect do |t| - t.automatically? ? {:to => t.destination, :auto => 'true'} : {:to => t.destination, :action => t.action} - end - out << { :name => state, :transitions => transitions } - end - out.to_json - end - format.html { haml :'instance_states/show'} - format.gv { erb :"instance_states/show" } - format.png do - # Trick respond_to into looking up the right template for the - # graphviz file - gv = erb(:"instance_states/show") - png = '' - cmd = 'dot -Kdot -Gpad="0.2,0.2" -Gsize="5.0,8.0" -Gdpi="180" -Tpng' - Open3.popen3( cmd ) do |stdin, stdout, stderr| - stdin.write( gv ) - stdin.close() - png = stdout.read - end - content_type 'image/png' - png - end - end - end - end -end - -get "#{settings.root_url}/instances/:id/run" do - @instance = driver.instance(credentials, :id => params[:id]) - respond_to do |format| - format.html { haml :"instances/run_command" } - end -end - -collection :load_balancers do - description "Load balancers" - - operation :new do - description "Form to create a new load balancer" - control do - @realms = driver.realms(credentials) - @instances = driver.instances(credentials) if driver_has_feature?(:register_instance, :load_balancers) - respond_to do |format| - format.html { haml :"load_balancers/new" } - end - end - end - - operation :index do - description "List of all active load balancers" - control do - filter_all :load_balancers - end - end - - operation :show do - description "Show details about given load balancer" - param :id, :string, :required - control { show :load_balancer } - end - - operation :create do - description "Create a new load balancer" - param :name, :string, :required - param :realm_id, :string, :required - param :listener_protocol, :string, :required, ['HTTP', 'TCP'] - param :listener_balancer_port, :string, :required - param :listener_instance_port, :string, :required - control do - @load_balancer = driver.create_load_balancer(credentials, params) - status 201 # Created - response['Location'] = load_balancer_url(@instance.id) - respond_to do |format| - format.xml { haml :"load_balancers/show" } - format.json { convert_to_json(:load_balancer, @load_balancer) } - format.html { haml :"load_balancers/show" } - end - end - end - - operation :register, :method => :post, :member => true do - description "Add instance to loadbalancer" - param :id, :string, :required - param :instance_id, :string, :required - control do - driver.lb_register_instance(credentials, params) - status 204 - respond_to do |format| - format.xml - format.json - format.html { redirect(load_balancer_url(params[:id])) } - end - end - end - - operation :unregister, :method => :post, :member => true do - description "Remove instance from loadbalancer" - param :id, :string, :required - param :instance_id, :string, :required - control do - driver.lb_unregister_instance(credentials, params) - status 204 - respond_to do |format| - format.xml - format.json - format.html { redirect(load_balancer_url(params[:id])) } - end - end - end - - operation :destroy do - description "Destroy given load balancer" - param :id, :string, :required - control do - driver.destroy_load_balancer(credentials, params[:id]) - status 204 - respond_to do |format| - format.xml - format.json - format.html { redirect(load_balancers_url) } - end - end - end - -end - - -collection :instances do - description <<END - An instance is a concrete machine realized from an image. - The images collection may be obtained by following the link from the primary entry-point. -END - - operation :new do - description "Form for creating a new instance resource" - param :image_id, :string, "Image from which will be the new instance created from" - param :realm_id, :string, :optional - if driver_has_feature? :authentication_key - param :authentication_key, :string, :optional - end - if driver_has_feature? :firewalls - param :firewalls, :string, :optional - end - control do - @instance = Instance.new( { :id=>params[:id], :image_id=>params[:image_id] } ) - @image = Image.new( :id => params[:image_id] ) - @hardware_profiles = driver.hardware_profiles(credentials, :architecture => @image.architecture ) - @realms = [Realm.new(:id => params[:realm_id])] if params[:realm_id] - @realms ||= driver.realms(credentials) - @keys = driver.keys(credentials) if driver_has_feature?(:authentication_key) - @firewalls = driver.firewalls(credentials) if driver_has_feature?(:firewalls) - respond_to do |format| - format.html do - haml :'instances/new' - end - end - end - end - - operation :index do - description "List all instances." - with_capability :instances - param :id, :string, :optional - param :state, :string, :optional - control { filter_all(:instances) } - end - - operation :show do - description 'Show an instance identified by "id" parameter.' - with_capability :instance - param :id, :string, :required - control { show(:instance) } - end - - operation :create do - description "Create a new instance." - with_capability :create_instance - param :image_id, :string, :required - param :realm_id, :string, :optional - param :hwp_id, :string, :optional - control do - @instance = driver.create_instance(credentials, params[:image_id], params) - if @instance.kind_of? Array - @elements = @instance - action_handler = "index" - else - response['Location'] = instance_url(@instance.id) - action_handler = "show" - end - status 201 # Created - respond_to do |format| - format.xml { haml :"instances/#{action_handler}" } - format.json do - if @elements - convert_to_json(:instances, @elements) - else - convert_to_json(:instance, @instance) - end - end - format.html do - if @elements - haml :"instances/index" - elsif @instance and @instance.id - response['Location'] = instance_url(@instance.id) - haml :"instances/show" - else - redirect instances_url - end - end - end - end - end - - operation :reboot, :method => :post, :member => true do - description "Reboot a running instance." - with_capability :reboot_instance - param :id, :string, :required - control { instance_action(:reboot) } - end - - operation :start, :method => :post, :member => true do - description "Start an instance." - with_capability :start_instance - param :id, :string, :required - control { instance_action(:start) } - end - - operation :stop, :method => :post, :member => true do - description "Stop a running instance." - with_capability :stop_instance - param :id, :string, :required - control { instance_action(:stop) } - end - - operation :destroy do - description "Destroy an instance." - with_capability :destroy_instance - param :id, :string, :required - control { instance_action(:destroy) } - end - - operation :run, :method => :post, :member => true do - description <<END - Run command on instance. Either password or private key must be send - in order to execute command. Authetication method should be advertised - in instance. -END - with_capability :run_on_instance - param :id, :string, :required - param :cmd, :string, :required, [], "Shell command to run on instance" - param :private_key, :string, :optional, [], "Private key in PEM format for authentication" - param :password, :string, :optional, [], "Password used for authentication" - control do - @output = driver.run_on_instance(credentials, params) - respond_to do |format| - format.xml { haml :"instances/run" } - format.html { haml :"instances/run" } - end - end - end -end - -collection :hardware_profiles do - description <<END - A hardware profile represents a configuration of resources upon which a - machine may be deployed. It defines aspects such as local disk storage, - available RAM, and architecture. Each provider is free to define as many - (or as few) hardware profiles as desired. -END - - operation :index do - description "List of available hardware profiles." - with_capability :hardware_profiles - param :id, :string - param :architecture, :string, :optional, [ 'i386', 'x86_64' ] - control do - @profiles = driver.hardware_profiles(credentials, params) - respond_to do |format| - format.xml { haml :'hardware_profiles/index' } - format.html { haml :'hardware_profiles/index' } - format.json { convert_to_json(:hardware_profile, @profiles) } - end - end - end - - operation :show do - description "Show specific hardware profile." - with_capability :hardware_profile - param :id, :string, :required - control do - @profile = driver.hardware_profile(credentials, params[:id]) - if @profile - respond_to do |format| - format.xml { haml :'hardware_profiles/show', :layout => false } - format.html { haml :'hardware_profiles/show' } - format.json { convert_to_json(:hardware_profile, @profile) } - end - else - report_error(404) - end - end - end - -end - -collection :storage_snapshots do - description "Storage snapshots description here" - - operation :new do - description "A form to create a new storage snapshot" - control do - respond_to do |format| - format.html { haml :"storage_snapshots/new" } - end - end - end - - operation :index do - description "List of storage snapshots." - with_capability :storage_snapshots - param :id, :string - control { filter_all(:storage_snapshots) } - end - - operation :show do - description "Show storage snapshot." - with_capability :storage_snapshot - param :id, :string, :required - control { show(:storage_snapshot) } - end - - operation :create do - description "Create a new snapshot from volume" - with_capability :create_storage_snapshot - param :volume_id, :string, :required - control do - @storage_snapshot = driver.create_storage_snapshot(credentials, params) - status 201 # Created - response['Location'] = storage_snapshot_url(@storage_snapshot.id) - show(:storage_snapshot) - end - end - - operation :destroy do - description "Delete storage snapshot" - with_capability :destroy_storage_snapshot - param :id, :string, :required - control do - driver.destroy_storage_snapshot(credentials, params) - status 204 - respond_to do |format| - format.xml - format.json - format.html { redirect(storage_snapshots_url) } - end - end - end -end - -collection :storage_volumes do - description "Storage volumes description here" - - operation :new do - description "A form to create a new storage volume" - control do - respond_to do |format| - format.html { haml :"storage_volumes/new" } - end - end - end - - operation :index do - description "List of storage volumes." - with_capability :storage_volumes - param :id, :string - control { filter_all(:storage_volumes) } - end - - operation :show do - description "Show storage volume." - with_capability :storage_volume - param :id, :string, :required - control { show(:storage_volume) } - end - - operation :create do - description "Create a new storage volume" - with_capability :create_storage_volume - param :snapshot_id, :string, :optional - param :capacity, :string, :optional - param :realm_id, :string, :optional - control do - @storage_volume = driver.create_storage_volume(credentials, params) - status 201 - response['Location'] = storage_volume_url(@storage_volume.id) - respond_to do |format| - format.xml { haml :"storage_volumes/show" } - format.html { haml :"storage_volumes/show" } - format.json { convert_to_json(:storage_volume, @storage_volume) } - end - end - end - - operation :attach_instance, :method=>:get, :member=>true do - description "A form to attach a storage volume to an instance" - control do - @instances = driver.instances(credentials) - respond_to do |format| - format.html{ haml :"storage_volumes/attach"} - end - end - end - - operation :attach, :method => :post, :member => true do - description "Attach storage volume to instance" - with_capability :attach_storage_volume - param :id, :string, :required - param :instance_id,:string, :required - param :device, :string, :required - control do - @storage_volume = driver.attach_storage_volume(credentials, params) - status 202 - respond_to do |format| - format.html { redirect(storage_volume_url(params[:id]))} - format.xml { haml :"storage_volumes/show" } - format.json { convert_to_json(:storage_volume, @storage_volume) } - end - end - end - - operation :detach, :method => :post, :member => true do - description "Detach storage volume to instance" - with_capability :detach_storage_volume - param :id, :string, :required - control do - volume = driver.storage_volume(credentials, :id => params[:id]) - @storage_volume = driver.detach_storage_volume(credentials, :id => volume.id, :instance_id => volume.instance_id, :device => volume.device) - status 202 - respond_to do |format| - format.html { redirect(storage_volume_url(params[:id]))} - format.xml { haml :"storage_volumes/show" } - format.json { convert_to_json(:storage_volume, @storage_volume) } - end - end - end - - operation :destroy do - description "Destroy storage volume" - with_capability :destroy_storage_volume - param :id, :string, :optional - control do - driver.destroy_storage_volume(credentials, params) - status 204 - respond_to do |format| - format.xml - format.json - format.html { redirect(storage_volumes_url) } - end - end - end - -end - -collection :keys do - description "Instance authentication credentials." - - operation :new do - description "A form to create a new key resource" - control do - respond_to do |format| - format.html { haml :"keys/new" } - end - end - end - - operation :index do - description "List all available credentials which could be used for instance authentication." - with_capability :keys - control do - filter_all :keys - end - end - - operation :show do - description "Show details about given instance credential." - with_capability :key - param :id, :string, :required - control { show :key } - end - - operation :create do - description "Create a new instance credential if backend supports this." - with_capability :create_key - param :name, :string, :required - control do - @key = driver.create_key(credentials, { :key_name => params[:name] }) - status 201 - response['Location'] = key_url(@key.id) - respond_to do |format| - format.xml { haml :"keys/show", :ugly => true } - format.html { haml :"keys/show" } - format.json { convert_to_json(:key, @key)} - end - end - end - - operation :destroy do - description "Destroy given instance credential if backend supports this." - with_capability :destroy_key - param :id, :string, :required - control do - driver.destroy_key(credentials, { :id => params[:id]}) - status 204 - respond_to do |format| - format.xml - format.json - format.html { redirect(keys_url) } - end - end - end - -end - -#get html form for creating a new blob - -# The URL for getting the new blob form for the HTML UI looks like the URL -# for getting the details of an existing blob. To make collisions less -# likely, we use a name for the form that will rarely be the name of an -# existing blob -NEW_BLOB_FORM_ID = "new_blob_form_d15cfd90" - -get "#{settings.root_url}/buckets/:bucket/#{NEW_BLOB_FORM_ID}" do - @bucket_id = params[:bucket] - respond_to do |format| - format.html {haml :"blobs/new"} - end -end - -collection :buckets do - description "Cloud Storage buckets - aka buckets|directories|folders" - - collection :blobs do - description "Blobs associated with given bucket" - - operation :show do - description "Display blob" - control do - @blob = driver.blob(credentials, { :id => params[:blob], 'bucket' => params[:bucket]}) - if @blob - respond_to do |format| - format.xml { haml :"blobs/show" } - format.html { haml :"blobs/show" } - format.json { convert_to_json(:blob, @blob) } - end - else - report_error(404) - end - end - - end - - operation :create do - description "Create new blob" - param :blob_id, :string, :required - param :blob_data, :hash, :required - control do - bucket_id = params[:bucket] - blob_id = params['blob_id'] - blob_data = params['blob_data'] - user_meta = {} - #metadata from params (i.e., passed by http form post, e.g. browser) - max = params[:meta_params] - if(max) - (1..max.to_i).each do |i| - key = params[:"meta_name#{i}"] - key = "HTTP_X_Deltacloud_Blobmeta_#{key}" - value = params[:"meta_value#{i}"] - user_meta[key] = value - end - end - @blob = driver.create_blob(credentials, bucket_id, blob_id, blob_data, user_meta) - respond_to do |format| - format.xml { haml :"blobs/show" } - format.html { haml :"blobs/show"} - format.json {convert_to_json(:blob, @blob)} - end - end - end - - operation :destroy do - description "Destroy given blob" - control do - bucket_id = params[:bucket] - blob_id = params[:blob] - driver.delete_blob(credentials, bucket_id, blob_id) - status 204 - respond_to do |format| - format.xml - format.json - format.html { redirect(bucket_url(bucket_id)) } - end - end - end - - operation :stream, :member => true, :standard => true, :method => :put do - description "Stream new blob data into the blob" - control do - if(env["BLOB_SUCCESS"]) #ie got a 200ok after putting blob - content_type = env["CONTENT_TYPE"] - content_type ||= "" - @blob = driver.blob(credentials, {:id => params[:blob], - 'bucket' => params[:bucket]}) - respond_to do |format| - format.xml { haml :"blobs/show" } - format.html { haml :"blobs/show" } - format.json { convert_to_json(:blob, @blob) } - end - elsif(env["BLOB_FAIL"]) - report_error(500) #OK? - else # small blobs - < 112kb dont hit the streaming monkey patch - use 'normal' create_blob - # also, if running under webrick don't hit the streaming patch (Thin specific) - bucket_id = params[:bucket] - blob_id = params[:blob] - temp_file = Tempfile.new("temp_blob_file") - temp_file.write(env['rack.input'].read) - temp_file.flush - content_type = env['CONTENT_TYPE'] || "" - blob_data = {:tempfile => temp_file, :type => content_type} - user_meta = BlobHelper::extract_blob_metadata_hash(request.env) - @blob = driver.create_blob(credentials, bucket_id, blob_id, blob_data, user_meta) - temp_file.delete - respond_to do |format| - format.xml { haml :"blobs/show" } - format.html { haml :"blobs/show" } - format.json { convert_to_json(:blob, @blob) } - end - end - end - end - - operation :metadata, :member => true, :standard => true, :method => :head do - description "Get blob metadata" - control do - @blob_id = params[:blob] - @blob_metadata = driver.blob_metadata(credentials, {:id => params[:blob], 'bucket' => params[:bucket]}) - if @blob_metadata - @blob_metadata.each do |k,v| - headers["X-Deltacloud-Blobmeta-#{k}"] = v - end - status 204 - respond_to do |format| - format.xml - format.json - end - else - report_error(404) - end - end - end - - operation :update, :member => true, :method => :post do - description "Update blob metadata" - control do - meta_hash = BlobHelper::extract_blob_metadata_hash(request.env) - success = driver.update_blob_metadata(credentials, {'bucket'=>params[:bucket], :id =>params[:blob], 'meta_hash' => meta_hash}) - if(success) - meta_hash.each do |k,v| - headers["X-Deltacloud-Blobmeta-#{k}"] = v - end - status 204 - respond_to do |format| - format.xml - format.json - end - else - report_error(404) #FIXME is this the right error code? - end - end - end +require 'sinatra/base' +require 'sinatra/rabbit' - operation :content, :member => true, :method => :get do - description "Download blob content" - control do - @blob = driver.blob(credentials, { :id => params[:blob], 'bucket' => params[:bucket]}) - if @blob - params['content_length'] = @blob.content_length - params['content_type'] = @blob.content_type - params['content_disposition'] = "attachment; filename=#{@blob.id}" - BlobStream.call(env, credentials, params) - else - report_error(404) - end - end - end +require_relative '../sinatra' +require_relative './models' +require_relative './drivers' +require_relative './helpers' +require_relative './collections' - end - operation :new do - description "A form to create a new bucket resource" - control do - respond_to do |format| - format.html { haml :"buckets/new" } - end - end - end +module Deltacloud + class API < Collections::Base - operation :index do - description "List buckets associated with this account" - with_capability :buckets - param :id, :string - param :name, :string - param :size, :string - control { filter_all(:buckets) } - end + # Enable logging + use Rack::CommonLogger + use Rack::Date + use Rack::ETag + use Rack::MatrixParams + use Rack::DriverSelect + use Rack::Accept + use Rack::MediaType - operation :show do - description "Show bucket" - with_capability :bucket - param :id, :string - control { show(:bucket) } - end + include Deltacloud::Helpers + include Deltacloud::Collections - operation :create do - description "Create a new bucket (POST /api/buckets)" - with_capability :create_bucket - param :name, :string, :required - control do - @bucket = driver.create_bucket(credentials, params[:name], params) - status 201 - response['Location'] = bucket_url(@bucket.id) - respond_to do |format| - format.xml { haml :"buckets/show" } - format.json { convert_to_json(:bucket, @bucket) } - format.html do - redirect bucket_url(@bucket.id) if @bucket and @bucket.id - redirect buckets_url - end + get API_ROOT_URL do + if params[:force_auth] + return [401, 'Authentication failed'] unless driver.valid_credentials?(credentials) end - end - end - - operation :destroy do - description "Delete a bucket by name - bucket must be empty" - with_capability :delete_bucket - param :id, :string, :required - control do - driver.delete_bucket(credentials, params[:id], params) - status 204 respond_to do |format| - format.xml - format.json - format.html { redirect(buckets_url) } + format.xml { haml :"api/show" } + format.json { xml_to_json :"api/show" } + format.html { haml :"api/show" } end end - end - -end -get "#{settings.root_url}/addresses/:id/associate" do - @instances = driver.instances(credentials) - @address = Address::new(:id => params[:id]) - respond_to do |format| - format.html { haml :"addresses/associate" } end end -collection :addresses do - description "Manage IP addresses" - - operation :index do - description "List IP addresses assigned to your account." - with_capability :addresses - control do - filter_all :addresses - end - end - - operation :show do - description "Show details about IP addresses specified by given ID" - with_capability :address - param :id, :string, :required - control { show :address } - end - - operation :create do - description "Acquire a new IP address for use with your account." - with_capability :create_address - control do - @address = driver.create_address(credentials, {}) - status 201 # Created - response['Location'] = address_url(@address.id) - respond_to do |format| - format.xml { haml :"addresses/show", :ugly => true } - format.html { haml :"addresses/_address", :layout => false } - format.json { convert_to_json(:address, @address) } - end - end - end - - operation :destroy do - description "Release an IP address associated with your account" - with_capability :destroy_address - param :id, :string, :required - control do - driver.destroy_address(credentials, { :id => params[:id]}) - status 204 - respond_to do |format| - format.xml - format.json - format.html { redirect(addresses_url) } - end - end - end - - operation :associate, :method => :post, :member => true do - description "Associate an IP address to an instance" - with_capability :associate_address - param :id, :string, :required - param :instance_id, :string, :required - control do - driver.associate_address(credentials, { :id => params[:id], :instance_id => params[:instance_id]}) - status 202 # Accepted - respond_to do |format| - format.xml - format.json - format.html { redirect(address_url(params[:id])) } - end - end - end - - operation :disassociate, :method => :post, :member => true do - description "Disassociate an IP address from an instance" - with_capability :associate_address - param :id, :string, :required - control do - driver.disassociate_address(credentials, { :id => params[:id] }) - status 202 # Accepted - respond_to do |format| - format.xml - format.json - format.html { redirect(address_url(params[:id])) } - end - end - end - -end - -#delete a firewall rule -delete '/api/firewalls/:firewall/:rule' do - opts = {} - opts[:firewall] = params[:firewall] - opts[:rule_id] = params[:rule] - driver.delete_firewall_rule(credentials, opts) - status 204 - respond_to do |format| - format.xml - format.json - format.html {redirect firewall_url(params[:firewall])} - end -end - -#FIREWALLS -collection :firewalls do - description "Allow user to define firewall rules for an instance (ec2 security groups) eg expose ssh access [port 22, tcp]." - - operation :new do - description "A form to create a new firewall resource" - control do - respond_to do |format| - format.html { haml :"firewalls/new" } - end - end - end - - operation :new_rule, :form => true, :member => true, :method => :get do - description "A form to create a new firewall rule" - param :id, :string, :required - control do - @firewall_name = params[:id] - respond_to do |format| - format.html {haml :"firewalls/new_rule" } - end - end - end - - operation :index do - description 'List all firewalls' - with_capability :firewalls - control { filter_all(:firewalls) } - end - - operation :show do - description 'Show details for a specific firewall - list all rules' - with_capability :firewall - param :id, :string, :required - control { show(:firewall) } - end - - operation :create do - description 'Create a new firewall' - with_capability :create_firewall - param :name, :string, :required - param :description, :string, :required - control do - @firewall = driver.create_firewall(credentials, params ) - status 201 # Created - response['Location'] = firewall_url(@firewall.id) - respond_to do |format| - format.xml { haml :"firewalls/show" } - format.html { haml :"firewalls/show" } - format.json { convert_to_json(:firewall, @firewall) } - end - end - end - - operation :destroy do - description 'Delete a specified firewall - error if firewall has rules' - with_capability :delete_firewall - param :id, :string, :required - control do - driver.delete_firewall(credentials, params) - status 204 - respond_to do |format| - format.xml - format.json - format.html { redirect(firewalls_url) } - end - end - end - - #create a new firewall rule - POST /api/firewalls/:firewall/rules - operation :rules, :method => :post, :member => true do - description 'Create a new firewall rule for the specified firewall' - param :id, :required, :string, [], "Name of firewall in which to apply this rule" - param :protocol, :required, :string, ['tcp','udp','icmp'], "Transport layer protocol for the rule" - param :port_from, :required, :string, [], "Start of port range for the rule" - param :port_to, :required, :string, [], "End of port range for the rule" - with_capability :create_firewall_rule - control do - #source IPs from params - addresses = params.inject([]){|result,current| result << current.last unless current.grep(/^ip[-_]address/i).empty?; result} - #source groups from params - groups = {} - max_groups = params.select{|k,v| k=~/^group/}.size/2 - for i in (1..max_groups) do - groups.merge!({params["group#{i}"]=>params["group#{i}owner"]}) - end - params['addresses'] = addresses - params['groups'] = groups - if addresses.empty? && groups.empty? - raise Deltacloud::ExceptionHandler::ValidationFailure.new( - StandardError.new("No sources. Specify at least one source ip_address or group") - ) - end - driver.create_firewall_rule(credentials, params) - @firewall = driver.firewall(credentials, {:id => params[:id]}) - status 201 - respond_to do |format| - format.xml { haml :"firewalls/show" } - format.html { haml :"firewalls/show" } - format.json { convert_to_json(:firewall, @firewall) } - end - end - end - -end -- 1.7.10
