From 7bef5e78dbae33cdd666c572888d0f3c2ba40dd2 Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Mon, 16 Jul 2012 18:16:39 +0200 Subject: [PATCH 01/13] feature #378: Add EBS support --- install.sh | 6 + src/cloud/ec2/etc/econe.conf | 11 +- src/cloud/ec2/lib/EC2QueryServer.rb | 26 ++-- src/cloud/ec2/lib/ImageEC2.rb | 49 +++++- src/cloud/ec2/lib/ebs.rb | 152 +++++++++++++++++++ src/cloud/ec2/lib/econe-server.rb | 10 ++ src/cloud/ec2/lib/views/attach_volume.erb | 9 ++ src/cloud/ec2/lib/views/create_volume.erb | 10 ++ src/cloud/ec2/lib/views/delete_volume.erb | 5 + src/cloud/ec2/lib/views/describe_images.erb | 2 +- src/cloud/ec2/lib/views/describe_volumes.erb | 22 +++ src/cloud/ec2/lib/views/detach_volume.erb | 9 ++ 12 files changed, 288 insertions(+), 23 deletions(-) create mode 100644 src/cloud/ec2/lib/ebs.rb create mode 100644 src/cloud/ec2/lib/views/attach_volume.erb create mode 100644 src/cloud/ec2/lib/views/create_volume.erb create mode 100644 src/cloud/ec2/lib/views/delete_volume.erb create mode 100644 src/cloud/ec2/lib/views/describe_volumes.erb create mode 100644 src/cloud/ec2/lib/views/detach_volume.erb diff --git a/install.sh b/install.sh index 11b440c206..857fa71b3e 100755 --- a/install.sh +++ b/install.sh @@ -1071,12 +1071,18 @@ ECO_LIB_FILES="src/cloud/ec2/lib/EC2QueryClient.rb \ src/cloud/ec2/lib/EC2QueryServer.rb \ src/cloud/ec2/lib/ImageEC2.rb \ src/cloud/ec2/lib/elastic_ip.rb \ + src/cloud/ec2/lib/ebs.rb \ src/cloud/ec2/lib/econe-server.rb" ECO_LIB_CLIENT_FILES="src/cloud/ec2/lib/EC2QueryClient.rb" ECO_LIB_VIEW_FILES="src/cloud/ec2/lib/views/describe_images.erb \ src/cloud/ec2/lib/views/describe_instances.erb \ + src/cloud/ec2/lib/views/create_volume.erb \ + src/cloud/ec2/lib/views/describe_volumes.erb \ + src/cloud/ec2/lib/views/attach_volume.erb \ + src/cloud/ec2/lib/views/detach_volume.erb \ + src/cloud/ec2/lib/views/delete_volume.erb \ src/cloud/ec2/lib/views/register_image.erb \ src/cloud/ec2/lib/views/run_instances.erb \ src/cloud/ec2/lib/views/allocate_address.erb \ diff --git a/src/cloud/ec2/etc/econe.conf b/src/cloud/ec2/etc/econe.conf index d8329b9f52..527953d480 100644 --- a/src/cloud/ec2/etc/econe.conf +++ b/src/cloud/ec2/etc/econe.conf @@ -60,7 +60,7 @@ # Cluster associated with the EC2 resources, by default no Cluster is defined #:cluster_id: -# Datastore in which the Images uploaded through EC2 will be allocated, by +# Datastore in which the Images uploaded through EC2 will be allocated, by # default 1 #:datastore_id: @@ -76,7 +76,7 @@ # VirtualNetwork containing the elastic ips to be used with EC2. If no defined # the Elastic IP functionality is disabled -#:elasticips_vnet_id: +#:elasticips_vnet_id: # Script to associate a public IP with a private IP # - arguments: elastic_ip private_ip vnet_template(base64_encoded) @@ -85,3 +85,10 @@ # Script to disassociate a public IP # - arguments: elastic_ip :disassociate_script: /usr/bin/false + +############################################################# +# EBS +############################################################# + +# FSTYPE that will be used when creating new volumes (DATABLOCKs) +:ebs_fstype: ext3 diff --git a/src/cloud/ec2/lib/EC2QueryServer.rb b/src/cloud/ec2/lib/EC2QueryServer.rb index 9e0a7bfde6..b0de206b6b 100644 --- a/src/cloud/ec2/lib/EC2QueryServer.rb +++ b/src/cloud/ec2/lib/EC2QueryServer.rb @@ -59,18 +59,6 @@ class EC2QueryServer < CloudServer 'unkn' => :terminated } - EC2_IMAGE_STATES={ - "INIT" => "pending", - "READY" => "available", - "USED" => "available", - "DISABLED" => nil, - "LOCKED" => "pending", - "ERROR" => "failed", - "CLONE" => "available", - "DELETE" => nil, - "USED_PERS" => "available" - } - ########################################################################### def initialize(client, oneadmin_client, config, logger) @@ -85,6 +73,9 @@ class EC2QueryServer < CloudServer require 'elastic_ip' extend ElasticIP end + + require 'ebs' + extend EBS end ########################################################################### @@ -92,7 +83,10 @@ class EC2QueryServer < CloudServer ########################################################################### def upload_image(params) - image = ImageEC2.new(Image.build_xml, @client, params['file']) + image = ImageEC2.new(Image.build_xml, + @client, + params['file'], + {:type => "OS"}) template = image.to_one_template if OpenNebula.is_error?(template) @@ -132,7 +126,7 @@ class EC2QueryServer < CloudServer def describe_images(params) user_flag = OpenNebula::Pool::INFO_ALL - impool = ImagePool.new(@client, user_flag) + impool = ImageEC2Pool.new(@client, user_flag) impool.info erb_version = params['Version'] @@ -263,9 +257,7 @@ class EC2QueryServer < CloudServer #{ec2_state[:name]}" end - def render_image_state(image) - EC2_IMAGE_STATES[image.state_str] - end + def render_launch_time(vm) return "#{Time.at(vm["STIME"].to_i).xmlschema}" diff --git a/src/cloud/ec2/lib/ImageEC2.rb b/src/cloud/ec2/lib/ImageEC2.rb index 7ec262bca9..214089a970 100644 --- a/src/cloud/ec2/lib/ImageEC2.rb +++ b/src/cloud/ec2/lib/ImageEC2.rb @@ -19,19 +19,50 @@ require 'OpenNebula' include OpenNebula +class ImageEC2Pool < ImagePool + def initialize(client, user_id=-1) + super(client, user_id) + end + + def factory(element_xml) + ImageEC2.new(element_xml,@client) + end +end + class ImageEC2 < Image + EC2_IMAGE_STATES={ + "INIT" => "pending", + "READY" => "available", + "USED" => "available", + "DISABLED" => nil, + "LOCKED" => "pending", + "ERROR" => "failed", + "CLONE" => "available", + "DELETE" => nil, + "USED_PERS" => "in-use" + } + ONE_IMAGE = %q{ NAME = "ec2-<%= uuid %>" - TYPE = OS + TYPE = <%= @image_info[:type] %> + <% if @image_info[:size] != nil %> + SIZE = "<%= @image_info[:size] %>" + <% end %> + <% if @image_info[:fstype] != nil %> + FSTYPE = "<%= @image_info[:fstype] %>" + <% end %> + <% if @image_info[:persistent] != nil %> + PERSISTENT = "YES" + <% end %> <% if @image_file != nil %> PATH = "<%= @image_file %>" <% end %> }.gsub(/^ /, '') - def initialize(xml, client, file=nil) + def initialize(xml, client, file=nil, opts={}) super(xml, client) - @image_info = nil + @image_info = opts @image_file = file if file && file[:tempfile] @@ -45,4 +76,16 @@ class ImageEC2 < Image one = ERB.new(ONE_IMAGE) return one.result(binding) end + + def render_state + EC2_IMAGE_STATES[self.state_str] + end + + def render_size + self['SIZE'].to_i/1024 + end + + def render_create_time + Time.at(self["REGTIME"].to_i).xmlschema + end end diff --git a/src/cloud/ec2/lib/ebs.rb b/src/cloud/ec2/lib/ebs.rb new file mode 100644 index 0000000000..ba9dee4b18 --- /dev/null +++ b/src/cloud/ec2/lib/ebs.rb @@ -0,0 +1,152 @@ +module EBS + # Default FSTYPE when creating new volumes + DEFAULT_FSTYPE = "ext3" + + # Detaches a DATABLOCK from a VM + # + # @param [Hash] params + # @option params [String] VolumeId The ID of the DATABLOCK + # @option params [String] InstanceId The ID of the VM + # @option params [String] Device The TARGET (unsupported) + # @option params [String] Force The TARGET (unsupported) + def detach_volume(params) + image_id = params['VolumeId'] + image_id = image_id.split('-')[1] if image_id[0]==?v + + vm_id = params['InstanceId'] + vm_id = vm_id.split('-')[1] if vm_id[0]==?i + + target = params['Device'] + + vm = VirtualMachine.new(VirtualMachine.build_xml(vm_id), @client) + rc = vm.info + + return OpenNebula::Error.new('Unsupported'),400 if OpenNebula::is_error?(rc) + + disk_id = vm["TEMPLATE/DISK[IMAGE_ID=#{image_id.to_i}]/DISK_ID"] + + logger.debug { "Detaching DISK: #{disk_id} VM: #{vm_id} IMAGE: #{image_id}" } + + return OpenNebula::Error.new('Unsupported'),400 if disk_id.nil? + + rc = vm.detachdisk(disk_id.to_i) + + return OpenNebula::Error.new('Unsupported'),400 if OpenNebula::is_error?(rc) + + erb_version = params['Version'] + + response = ERB.new(File.read(@config[:views]+"/detach_volume.erb")) + return response.result(binding), 200 + end + + # Attaches a DATABLOCK to a running VM and exposes it as the specified + # device. + # + # @param [Hash] params + # @option params [String] VolumeId The ID of the DATABLOCK + # @option params [String] InstanceId The ID of the VM to which the + # volume attaches + # @option params [String] Device How the device is exposed to the + # instance (e.g., /dev/sdh, or xvdh) + def attach_volume(params) + image_id = params['VolumeId'] + image_id = image_id.split('-')[1] if image_id[0]==?v + + vm_id = params['InstanceId'] + vm_id = vm_id.split('-')[1] if vm_id[0]==?i + + target = params['Device'] + if m = target.match(/^\/dev\/(\w+)$/) + target = m[1] + end + + vm = VirtualMachine.new(VirtualMachine.build_xml(vm_id), @client) + rc = vm.info + + return OpenNebula::Error.new('Unsupported'),400 if OpenNebula::is_error?(rc) + + template = "DISK = [ IMAGE_ID = #{image_id}, TARGET = #{target} ]" + vm.attachdisk(template) + + return OpenNebula::Error.new('Unsupported'),400 if OpenNebula::is_error?(rc) + + erb_version = params['Version'] + + vm.info + + response = ERB.new(File.read(@config[:views]+"/attach_volume.erb")) + return response.result(binding), 200 + end + + # Creates a new DATABLOCK that any VM can attach to + # + # @param [Hash] params + # @option params [String] Size The size of the volume, in GiBs. + # @option params [String] SnapshotId The snapshot from which to create + # the new volume (unsupported). + # @option params [String] AvailabilityZone The Availability Zone in which + # to create the new volume (unsupported) + def create_volume(params) + size = params['Size'].to_i # in GiBs + size *= 1024 + + opts = { + :type => "DATABLOCK", + :size => size, + :fstype => @config[:ebs_fstype]||DEFAULT_FSTYPE, + :persistent => "YES" + } + + image = ImageEC2.new(Image.build_xml, @client, nil, opts) + + template = image.to_one_template + if OpenNebula.is_error?(template) + return OpenNebula::Error.new('Unsupported'), 400 + end + + rc = image.allocate(template, @config[:datastore_id]||1) + if OpenNebula.is_error?(rc) + return OpenNebula::Error.new('Unsupported'), 400 + end + + erb_version = params['Version'] + + image.info + + response = ERB.new(File.read(@config[:views]+"/create_volume.erb")) + return response.result(binding), 200 + end + + # Deletes a DATABLOCK + # + # @param [Hash] params + # @option params [String] VolumeId The ID of the DATABLOCK + def delete_volume(params) + image_id = params['VolumeId'] + image_id = image_id.split('-')[1] if image_id[0]==?v + + image = ImageEC2.new(Image.build_xml(image_id), @client) + rc = image.delete + + return OpenNebula::Error.new('Unsupported'),400 if OpenNebula::is_error?(rc) + + erb_version = params['Version'] + + response = ERB.new(File.read(@config[:views]+"/delete_volume.erb")) + return response.result(binding), 200 + end + + + # Describes your Amazon EBS volumes + def describe_volumes(params) + user_flag = OpenNebula::Pool::INFO_ALL + impool = ImageEC2Pool.new(@client, user_flag) + impool.info + + erb_version = params['Version'] + + response = ERB.new(File.read(@config[:views]+"/describe_volumes.erb")) + return response.result(binding), 200 + end + +end \ No newline at end of file diff --git a/src/cloud/ec2/lib/econe-server.rb b/src/cloud/ec2/lib/econe-server.rb index 953a11ffa0..cf57486939 100644 --- a/src/cloud/ec2/lib/econe-server.rb +++ b/src/cloud/ec2/lib/econe-server.rb @@ -207,6 +207,16 @@ def do_http_request(params) result,rc = @econe_server.release_address(params) when 'DescribeAddresses' result,rc = @econe_server.describe_addresses(params) + when 'CreateVolume' + result,rc = @econe_server.create_volume(params) + when 'DescribeVolumes' + result,rc = @econe_server.describe_volumes(params) + when 'AttachVolume' + result,rc = @econe_server.attach_volume(params) + when 'DetachVolume' + result,rc = @econe_server.detach_volume(params) + when 'DeleteVolume' + result,rc = @econe_server.delete_volume(params) end if OpenNebula::is_error?(result) diff --git a/src/cloud/ec2/lib/views/attach_volume.erb b/src/cloud/ec2/lib/views/attach_volume.erb new file mode 100644 index 0000000000..869202f933 --- /dev/null +++ b/src/cloud/ec2/lib/views/attach_volume.erb @@ -0,0 +1,9 @@ + + + + vol-<%= sprintf('%08i', image_id) %> + i-<%= sprintf('%08i', vm_id) %> + /dev/<%= vm["TEMPLATE/DISK[IMAGE_ID=#{image_id.to_i}]/TARGET"] %> + attaching + + diff --git a/src/cloud/ec2/lib/views/create_volume.erb b/src/cloud/ec2/lib/views/create_volume.erb new file mode 100644 index 0000000000..abe6895aae --- /dev/null +++ b/src/cloud/ec2/lib/views/create_volume.erb @@ -0,0 +1,10 @@ + + + + vol-<%= sprintf('%08i', image.id) %> + <%= image.render_size %> + + + <%= image.render_state %> + <%= image.render_create_time %> + \ No newline at end of file diff --git a/src/cloud/ec2/lib/views/delete_volume.erb b/src/cloud/ec2/lib/views/delete_volume.erb new file mode 100644 index 0000000000..52a6681cc5 --- /dev/null +++ b/src/cloud/ec2/lib/views/delete_volume.erb @@ -0,0 +1,5 @@ + + + + true + \ No newline at end of file diff --git a/src/cloud/ec2/lib/views/describe_images.erb b/src/cloud/ec2/lib/views/describe_images.erb index a38c99861d..bb590af948 100644 --- a/src/cloud/ec2/lib/views/describe_images.erb +++ b/src/cloud/ec2/lib/views/describe_images.erb @@ -2,7 +2,7 @@ <% impool.each do |im| %> - <% if state_image = render_image_state(im) %> + <% if state_image = im.render_state %> ami-<%= sprintf('%08i', im.id) %> <%= im['SOURCE'].split('/').last %> diff --git a/src/cloud/ec2/lib/views/describe_volumes.erb b/src/cloud/ec2/lib/views/describe_volumes.erb new file mode 100644 index 0000000000..539059d3b5 --- /dev/null +++ b/src/cloud/ec2/lib/views/describe_volumes.erb @@ -0,0 +1,22 @@ + + + + + <% impool.each do |im| %> + <% if state_image = im.render_state %> + + vol-<%= sprintf('%08i', im.id) %> + <%= im.render_size %> + + + <%= state_image %> + <%= im.render_create_time %> + + + + <% else + next + end + end %> + + \ No newline at end of file diff --git a/src/cloud/ec2/lib/views/detach_volume.erb b/src/cloud/ec2/lib/views/detach_volume.erb new file mode 100644 index 0000000000..0d8306a486 --- /dev/null +++ b/src/cloud/ec2/lib/views/detach_volume.erb @@ -0,0 +1,9 @@ + + + + vol-<%= sprintf('%08i', image_id) %> + i-<%= sprintf('%08i', vm_id) %> + <%= target %> + detaching + + From 696b1844f3b49493daa9ccea7c09c54aef0ed291 Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Tue, 17 Jul 2012 16:33:23 +0200 Subject: [PATCH 02/13] feature #378: Fix econe-*-addresses output --- src/cloud/ec2/bin/econe-allocate-address | 10 +++++----- src/cloud/ec2/bin/econe-describe-addresses | 12 ++++++------ src/cloud/ec2/lib/views/describe_addresses.erb | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/cloud/ec2/bin/econe-allocate-address b/src/cloud/ec2/bin/econe-allocate-address index a42ac55a53..a17ded43fd 100755 --- a/src/cloud/ec2/bin/econe-allocate-address +++ b/src/cloud/ec2/bin/econe-allocate-address @@ -50,7 +50,7 @@ Options: --headers, -H Display column headers - + EOT require 'econe/EC2QueryClient' @@ -96,7 +96,7 @@ begin end rescue Exception => e exit -1 -end +end auth = "#{access}:#{secret}" if secret && access @@ -115,11 +115,11 @@ if CloudClient::is_error?(addr) end if headers - puts "publicIP" - puts "------------------------------------------------------------------------------" + puts "publicIp" + puts "------------------------------------------------------------------------------" end -puts addr['publicIP'] +puts addr['publicIp'] exit 0 diff --git a/src/cloud/ec2/bin/econe-describe-addresses b/src/cloud/ec2/bin/econe-describe-addresses index c74e0e584e..f799407ed7 100755 --- a/src/cloud/ec2/bin/econe-describe-addresses +++ b/src/cloud/ec2/bin/econe-describe-addresses @@ -50,7 +50,7 @@ Options: --headers, -H Display column headers - + EOT require 'econe/EC2QueryClient' @@ -96,7 +96,7 @@ begin end rescue Exception => e exit -1 -end +end auth = "#{access}:#{secret}" if secret && access @@ -116,16 +116,16 @@ end addresses = rc['addressesSet']['item'] -fmt = "%-12s %s" +fmt = "%-12s %s" if headers puts fmt % ["publicIP", "instanceId"] - puts "------------------------------------------------------------------------------" + puts "------------------------------------------------------------------------------" end -if addresses +if addresses addresses.each { |addr| - puts fmt % [addr['publicIP'],addr['instanceID']] + puts fmt % [addr['publicIp'],addr['instanceId']] } end diff --git a/src/cloud/ec2/lib/views/describe_addresses.erb b/src/cloud/ec2/lib/views/describe_addresses.erb index 3b42278cca..494f29821a 100644 --- a/src/cloud/ec2/lib/views/describe_addresses.erb +++ b/src/cloud/ec2/lib/views/describe_addresses.erb @@ -1,5 +1,5 @@ - + 4ac62eaf-e266-4058-a970-2c01568cd417 <% vnet.each("LEASES/LEASE[USED=1 and VID=-1]") do |eip| %> @@ -8,7 +8,7 @@ <%= eip["IP"] %> standard <% if vm_id = vnet["TEMPLATE/EC2_ADDRESSES[IP=\"#{eip["IP"]}\"]/VMID"] %> - <%= vm_id %> + i-<%= sprintf('%08i', vm_id) %> <% else %> <% end %> From 14fcfc90917de77070a10eb74e4faaa79da468cf Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Tue, 17 Jul 2012 16:34:27 +0200 Subject: [PATCH 03/13] feature #378: Add econe-*-volume cli --- install.sh | 10 ++ src/cloud/ec2/bin/econe-attach-volume | 150 ++++++++++++++++++++++ src/cloud/ec2/bin/econe-create-volume | 138 ++++++++++++++++++++ src/cloud/ec2/bin/econe-delete-volume | 126 ++++++++++++++++++ src/cloud/ec2/bin/econe-describe-volumes | 133 +++++++++++++++++++ src/cloud/ec2/bin/econe-detach-volume | 156 +++++++++++++++++++++++ src/cloud/ec2/lib/EC2QueryClient.rb | 90 ++++++++++++- 7 files changed, 802 insertions(+), 1 deletion(-) create mode 100755 src/cloud/ec2/bin/econe-attach-volume create mode 100755 src/cloud/ec2/bin/econe-create-volume create mode 100755 src/cloud/ec2/bin/econe-delete-volume create mode 100755 src/cloud/ec2/bin/econe-describe-volumes create mode 100755 src/cloud/ec2/bin/econe-detach-volume diff --git a/install.sh b/install.sh index 857fa71b3e..d70daf408e 100755 --- a/install.sh +++ b/install.sh @@ -1094,8 +1094,13 @@ ECO_LIB_VIEW_FILES="src/cloud/ec2/lib/views/describe_images.erb \ ECO_BIN_FILES="src/cloud/ec2/bin/econe-server \ src/cloud/ec2/bin/econe-describe-images \ + src/cloud/ec2/bin/econe-describe-volumes \ src/cloud/ec2/bin/econe-describe-instances \ src/cloud/ec2/bin/econe-register \ + src/cloud/ec2/bin/econe-attach-volume \ + src/cloud/ec2/bin/econe-detach-volume \ + src/cloud/ec2/bin/econe-delete-volume \ + src/cloud/ec2/bin/econe-create-volume \ src/cloud/ec2/bin/econe-run-instances \ src/cloud/ec2/bin/econe-terminate-instances \ src/cloud/ec2/bin/econe-describe-addresses \ @@ -1107,7 +1112,12 @@ ECO_BIN_FILES="src/cloud/ec2/bin/econe-server \ ECO_BIN_CLIENT_FILES="src/cloud/ec2/bin/econe-describe-images \ src/cloud/ec2/bin/econe-describe-instances \ + src/cloud/ec2/bin/econe-describe-volumes \ src/cloud/ec2/bin/econe-register \ + src/cloud/ec2/bin/econe-attach-volume \ + src/cloud/ec2/bin/econe-detach-volume \ + src/cloud/ec2/bin/econe-delete-volume \ + src/cloud/ec2/bin/econe-create-volume \ src/cloud/ec2/bin/econe-run-instances \ src/cloud/ec2/bin/econe-terminate-instances \ src/cloud/ec2/bin/econe-describe-addresses \ diff --git a/src/cloud/ec2/bin/econe-attach-volume b/src/cloud/ec2/bin/econe-attach-volume new file mode 100755 index 0000000000..69db7c1daf --- /dev/null +++ b/src/cloud/ec2/bin/econe-attach-volume @@ -0,0 +1,150 @@ +#!/usr/bin/env ruby +# -------------------------------------------------------------------------- # +# Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org) # +# # +# Licensed 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. # +#--------------------------------------------------------------------------- # + +ONE_LOCATION=ENV["ONE_LOCATION"] + +if !ONE_LOCATION + RUBY_LIB_LOCATION="/usr/lib/one/ruby" +else + RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby" +end + + +$: << RUBY_LIB_LOCATION +$: << RUBY_LIB_LOCATION+"/cloud" + +COMMANDS_HELP=<<-EOT +econe-attach-volume + +Attaches a DATABLOCK to a running instance and exposes it as the specified device. + +Usage: + econe-attach-volume [OPTIONS] VOLUME-ID + +Options: + + --help, -h + Show help + + --access-key , -K + The username of the user + + --secret-key , -S + The password of the user + + --url , -U + Set url as the web service url to use + + --instance , -i + The ID of the instance to attach the volume to + + --device , -d e + exit -1 +end + +volume_id = ARGV.shift + +if !volume_id + puts "#{cmd_name}: missing VOLUME-ID parameter" + exit -1 +end + +auth = "#{access}:#{secret}" if secret && access + +begin + ec2_client = EC2QueryClient::Client.new(auth,url) +rescue Exception => e + puts "#{cmd_name}: #{e.message}" + exit -1 +end + +rc = ec2_client.attach_volume(volume_id, instance, device) + +if CloudClient::is_error?(rc) + puts "#{cmd_name}: #{rc.message}" + exit -1 +end + +fmt = "%-15s %-15s %-10s %s" + +if headers + puts fmt % ["volumeId", "instanceId", "status", "device"] + puts "------------------------------------------------------------------------------" +end + +puts fmt % [rc['volumeId'], rc['instanceId'], rc['status'], rc['device']] + +exit 0 diff --git a/src/cloud/ec2/bin/econe-create-volume b/src/cloud/ec2/bin/econe-create-volume new file mode 100755 index 0000000000..3a18eb86b1 --- /dev/null +++ b/src/cloud/ec2/bin/econe-create-volume @@ -0,0 +1,138 @@ +#!/usr/bin/env ruby +# -------------------------------------------------------------------------- # +# Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org) # +# # +# Licensed 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. # +#--------------------------------------------------------------------------- # + +ONE_LOCATION=ENV["ONE_LOCATION"] + +if !ONE_LOCATION + RUBY_LIB_LOCATION="/usr/lib/one/ruby" +else + RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby" +end + + +$: << RUBY_LIB_LOCATION +$: << RUBY_LIB_LOCATION+"/cloud" + +COMMANDS_HELP=<<-EOT +econe-create-volume + +Create a new DATABLOCK + +Usage: + econe-create-volume [OPTIONS] + +Options: + + --help, -h + Show help + + --access-key , -K + The username of the user + + --secret-key , -S + The password of the user + + --url , -U + Set url as the web service url to use + + --size , -s + The ID of the instance to attach the volume to + + --headers, -H + Display column headers + +EOT + +require 'econe/EC2QueryClient' +require 'CloudClient' +require 'getoptlong' + +include CloudCLI + +opts = GetoptLong.new( + ['--help', '-h',GetoptLong::NO_ARGUMENT], + ['--version', '-v',GetoptLong::NO_ARGUMENT], + ['--access-key', '-K',GetoptLong::REQUIRED_ARGUMENT], + ['--secret-key', '-S',GetoptLong::REQUIRED_ARGUMENT], + ['--url', '-U',GetoptLong::REQUIRED_ARGUMENT], + ['--size', '-s',GetoptLong::REQUIRED_ARGUMENT], + ['--headers', '-H',GetoptLong::NO_ARGUMENT] + ) + +headers = false +url = nil +access = nil +secret = nil +auth = nil +size = nil + +begin + opts.each do |opt, arg| + case opt + when '--help' + puts COMMANDS_HELP + return + when '--version' + puts CloudCLI.version_text + exit 0 + when '--access-key' + access = arg + when '--secret-key' + secret = arg + when '--url' + url = arg + when '--size' + size = arg + when '--headers' + headers = true + end + end +rescue Exception => e + exit -1 +end + +if !size + puts "#{cmd_name}: missing --size option" + exit -1 +end + +auth = "#{access}:#{secret}" if secret && access + +begin + ec2_client = EC2QueryClient::Client.new(auth,url) +rescue Exception => e + puts "#{cmd_name}: #{e.message}" + exit -1 +end + +rc = ec2_client.create_volume(size) + +if CloudClient::is_error?(rc) + puts "#{cmd_name}: #{rc.message}" + exit -1 +end + +fmt = "%-15s %-10s %s" + +if headers + puts fmt % ["volumeId", "size", "createTime"] + puts "------------------------------------------------------------------------------" +end + +puts fmt % [rc['volumeId'], rc['size'], rc['createTime']] + +exit 0 diff --git a/src/cloud/ec2/bin/econe-delete-volume b/src/cloud/ec2/bin/econe-delete-volume new file mode 100755 index 0000000000..6492e37b53 --- /dev/null +++ b/src/cloud/ec2/bin/econe-delete-volume @@ -0,0 +1,126 @@ +#!/usr/bin/env ruby +# -------------------------------------------------------------------------- # +# Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org) # +# # +# Licensed 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. # +#--------------------------------------------------------------------------- # + +ONE_LOCATION=ENV["ONE_LOCATION"] + +if !ONE_LOCATION + RUBY_LIB_LOCATION="/usr/lib/one/ruby" +else + RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby" +end + + +$: << RUBY_LIB_LOCATION +$: << RUBY_LIB_LOCATION+"/cloud" + +COMMANDS_HELP=<<-EOT +econe-delete-volume + +Deletes a DATABLOCK + +Usage: + econe-delete-volume [OPTIONS] VOLUME-ID + +Options: + + --help, -h + Show help + + --access-key , -K + The username of the user + + --secret-key , -S + The password of the user + + --url , -U + Set url as the web service url to use + + --headers, -H + Display column headers + +VOLUME-ID: The ID of the DATABLOCK + +EOT + +require 'econe/EC2QueryClient' +require 'CloudClient' +require 'getoptlong' + +include CloudCLI + +opts = GetoptLong.new( + ['--help', '-h',GetoptLong::NO_ARGUMENT], + ['--version', '-v',GetoptLong::NO_ARGUMENT], + ['--access-key', '-K',GetoptLong::REQUIRED_ARGUMENT], + ['--secret-key', '-S',GetoptLong::REQUIRED_ARGUMENT], + ['--url', '-U',GetoptLong::REQUIRED_ARGUMENT], + ['--headers', '-H',GetoptLong::NO_ARGUMENT] + ) + +headers = false +url = nil +access = nil +secret = nil +auth = nil + +begin + opts.each do |opt, arg| + case opt + when '--help' + puts COMMANDS_HELP + return + when '--version' + puts CloudCLI.version_text + exit 0 + when '--access-key' + access = arg + when '--secret-key' + secret = arg + when '--url' + url = arg + when '--headers' + headers = true + end + end +rescue Exception => e + exit -1 +end + +volume_id = ARGV.shift + +if !volume_id + puts "#{cmd_name}: missing VOLUME-ID parameter" + exit -1 +end + +auth = "#{access}:#{secret}" if secret && access + +begin + ec2_client = EC2QueryClient::Client.new(auth,url) +rescue Exception => e + puts "#{cmd_name}: #{e.message}" + exit -1 +end + +rc = ec2_client.delete_volume(volume_id) + +if CloudClient::is_error?(rc) + puts "#{cmd_name}: #{rc.message}" + exit -1 +end + +exit 0 diff --git a/src/cloud/ec2/bin/econe-describe-volumes b/src/cloud/ec2/bin/econe-describe-volumes new file mode 100755 index 0000000000..ea7e9042aa --- /dev/null +++ b/src/cloud/ec2/bin/econe-describe-volumes @@ -0,0 +1,133 @@ +#!/usr/bin/env ruby +# -------------------------------------------------------------------------- # +# Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org) # +# # +# Licensed 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. # +#--------------------------------------------------------------------------- # + +ONE_LOCATION=ENV["ONE_LOCATION"] + +if !ONE_LOCATION + RUBY_LIB_LOCATION="/usr/lib/one/ruby" +else + RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby" +end + + +$: << RUBY_LIB_LOCATION +$: << RUBY_LIB_LOCATION+"/cloud" + +COMMANDS_HELP=<<-EOT +econe-describe-volumes + +Describes your Amazon EBS volumes + +Usage: + econe-describe-volumes [OPTIONS] + +Options: + --help, -h + Show help + + --access-key , -K + The username of the user + + --secret-key , -S + The password of the user + + --url , -U + Set url as the web service url to use + + --headers, -H + Display column headers + +EOT + +require 'econe/EC2QueryClient' +require 'CloudClient' +require 'getoptlong' + +include CloudCLI + + +opts = GetoptLong.new( + ['--help', '-h',GetoptLong::NO_ARGUMENT], + ['--version', '-v',GetoptLong::NO_ARGUMENT], + ['--access-key', '-K',GetoptLong::REQUIRED_ARGUMENT], + ['--secret-key', '-S',GetoptLong::REQUIRED_ARGUMENT], + ['--url', '-U',GetoptLong::REQUIRED_ARGUMENT], + ['--headers', '-H',GetoptLong::NO_ARGUMENT] + ) + +headers = false +url = nil +access = nil +secret = nil +auth = nil + +begin + opts.each do |opt, arg| + case opt + when '--help' + puts COMMANDS_HELP + return + when '--version' + puts CloudCLI.version_text + exit 0 + when '--access-key' + access = arg + when '--secret-key' + secret = arg + when '--url' + url = arg + when '--headers' + headers = true + end + end +rescue Exception => e + exit -1 +end + +auth = "#{access}:#{secret}" if secret && access + +begin + ec2_client = EC2QueryClient::Client.new(auth,url) +rescue Exception => e + puts "#{cmd_name}: #{e.message}" + exit -1 +end + +rc = ec2_client.describe_volumes + +if CloudClient::is_error?(rc) + puts "#{cmd_name}: #{rc.message}" + exit -1 +end + +volumes = rc['volumeSet']['item'] + +fmt = "%-15s %-10s %s" + +if headers + puts fmt % ["volumeId", "size", "createTime"] + puts "------------------------------------------------------------------------------" +end + +if volumes + volumes.each { |vol| + puts fmt % [vol['volumeId'], vol['size'], vol['createTime']] + } +end + +exit 0 + diff --git a/src/cloud/ec2/bin/econe-detach-volume b/src/cloud/ec2/bin/econe-detach-volume new file mode 100755 index 0000000000..5238b3bb3d --- /dev/null +++ b/src/cloud/ec2/bin/econe-detach-volume @@ -0,0 +1,156 @@ +#!/usr/bin/env ruby +# -------------------------------------------------------------------------- # +# Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org) # +# # +# Licensed 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. # +#--------------------------------------------------------------------------- # + +ONE_LOCATION=ENV["ONE_LOCATION"] + +if !ONE_LOCATION + RUBY_LIB_LOCATION="/usr/lib/one/ruby" +else + RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby" +end + + +$: << RUBY_LIB_LOCATION +$: << RUBY_LIB_LOCATION+"/cloud" + +COMMANDS_HELP=<<-EOT +econe-detach-volume + +Detaches an Amazon EBS volume from an instance. Make sure to unmount any +file systems on the device within your operating system before detaching the volume + +Usage: + econe-detach-volume [OPTIONS] VOLUME-ID + +Options: + + --help, -h + Show help + + --access-key , -K + The username of the user + + --secret-key , -S + The password of the user + + --url , -U + Set url as the web service url to use + + --instance , -i + The ID of the instance to attach the volume to + + --device , -d e + exit -1 +end + +volume_id = ARGV.shift + +if !volume_id + puts "#{cmd_name}: missing VOLUME-ID parameter" + exit -1 +end + +if !instance + puts "#{cmd_name}: missing --instance option" + exit -1 +end + +auth = "#{access}:#{secret}" if secret && access + +begin + ec2_client = EC2QueryClient::Client.new(auth,url) +rescue Exception => e + puts "#{cmd_name}: #{e.message}" + exit -1 +end + +rc = ec2_client.detach_volume(volume_id, instance, device) + +if CloudClient::is_error?(rc) + puts "#{cmd_name}: #{rc.message}" + exit -1 +end + +fmt = "%-15s %-15s %-10s %s" + +if headers + puts fmt % ["volumeId", "instanceId", "status", "device"] + puts "------------------------------------------------------------------------------" +end + +puts fmt % [rc['volumeId'], rc['instanceId'], rc['status'], rc['device']] + +exit 0 diff --git a/src/cloud/ec2/lib/EC2QueryClient.rb b/src/cloud/ec2/lib/EC2QueryClient.rb index 2615bcf849..32170f10fc 100644 --- a/src/cloud/ec2/lib/EC2QueryClient.rb +++ b/src/cloud/ec2/lib/EC2QueryClient.rb @@ -268,7 +268,7 @@ module EC2QueryClient def associate_address(public_ip, instance_id) begin response = @ec2_connection.associate_address( - :public_ip => public_ip, + :public_ip => public_ip, :instance_id => instance_id) rescue Exception => e error = CloudClient::Error.new(e.message) @@ -305,5 +305,93 @@ module EC2QueryClient return response end + + ###################################################################### + # + # + ###################################################################### + def describe_volumes + begin + response = @ec2_connection.describe_volumes + rescue Exception => e + error = CloudClient::Error.new(e.message) + return error + end + + return response + end + + ###################################################################### + # + # + ###################################################################### + def attach_volume(volume, instance, device) + begin + response = @ec2_connection.attach_volume( + :volume_id => volume, + :instance_id => instance, + :device => device + ) + rescue Exception => e + error = CloudClient::Error.new(e.message) + return error + end + + return response + end + + ###################################################################### + # + # + ###################################################################### + def delete_volume(volume) + begin + response = @ec2_connection.delete_volume( + :volume_id => volume + ) + rescue Exception => e + error = CloudClient::Error.new(e.message) + return error + end + + return response + end + + ###################################################################### + # + # + ###################################################################### + def detach_volume(volume, instance, device) + begin + response = @ec2_connection.detach_volume( + :volume_id => volume, + :instance_id => instance, + :device => device + ) + rescue Exception => e + error = CloudClient::Error.new(e.message) + return error + end + + return response + end + + ###################################################################### + # + # + ###################################################################### + def create_volume(size) + begin + response = @ec2_connection.create_volume( + :size => size, + :availability_zone => 'default' + ) + rescue Exception => e + error = CloudClient::Error.new(e.message) + return error + end + + return response + end end end From 0fec25543ff3eec101674bf9c125c93c3aa42046 Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Tue, 17 Jul 2012 18:05:05 +0200 Subject: [PATCH 04/13] feature #378: Add EBS metadata to images --- src/cloud/ec2/bin/econe-describe-volumes | 11 ++++- src/cloud/ec2/lib/ebs.rb | 47 ++++++++++++++++++++ src/cloud/ec2/lib/views/describe_volumes.erb | 11 ++++- src/oca/ruby/OpenNebula/Image.rb | 7 +-- src/oca/ruby/OpenNebula/VirtualNetwork.rb | 3 +- 5 files changed, 72 insertions(+), 7 deletions(-) diff --git a/src/cloud/ec2/bin/econe-describe-volumes b/src/cloud/ec2/bin/econe-describe-volumes index ea7e9042aa..4b19b3f326 100755 --- a/src/cloud/ec2/bin/econe-describe-volumes +++ b/src/cloud/ec2/bin/econe-describe-volumes @@ -119,13 +119,20 @@ volumes = rc['volumeSet']['item'] fmt = "%-15s %-10s %s" if headers - puts fmt % ["volumeId", "size", "createTime"] + puts fmt % ["volumeId", "size", "instanceId"] puts "------------------------------------------------------------------------------" end if volumes volumes.each { |vol| - puts fmt % [vol['volumeId'], vol['size'], vol['createTime']] + instances = "" + if vol['attachmentSet']['item'] + instances = vol['attachmentSet']['item'].collect{ |item| + item['instanceId'] + }.join(',') + end + + puts fmt % [vol['volumeId'], vol['size'], instances] } end diff --git a/src/cloud/ec2/lib/ebs.rb b/src/cloud/ec2/lib/ebs.rb index ba9dee4b18..debb91f1ad 100644 --- a/src/cloud/ec2/lib/ebs.rb +++ b/src/cloud/ec2/lib/ebs.rb @@ -18,6 +18,9 @@ module EBS target = params['Device'] + + # Detach + vm = VirtualMachine.new(VirtualMachine.build_xml(vm_id), @client) rc = vm.info @@ -33,6 +36,24 @@ module EBS return OpenNebula::Error.new('Unsupported'),400 if OpenNebula::is_error?(rc) + + # Update IMAGE metadata + + image = Image.new(Image.build_xml(image_id), @client) + rc = image.info + + return OpenNebula::Error.new('Unsupported'),400 if OpenNebula::is_error?(rc) + + image.delete_element("TEMPLATE/EBS[INSTANCE_ID=\"#{params['InstanceId']}\"]") + rc = image.update + if OpenNebula::is_error?(rc) + logger.error {rc.message} + return OpenNebula::Error.new('Unsupported'),400 + end + + + # Response + erb_version = params['Version'] response = ERB.new(File.read(@config[:views]+"/detach_volume.erb")) @@ -60,6 +81,9 @@ module EBS target = m[1] end + + # Attach + vm = VirtualMachine.new(VirtualMachine.build_xml(vm_id), @client) rc = vm.info @@ -70,6 +94,29 @@ module EBS return OpenNebula::Error.new('Unsupported'),400 if OpenNebula::is_error?(rc) + + # Update IMAGE metadata + + image = Image.new(Image.build_xml(image_id), @client) + rc = image.info + + return OpenNebula::Error.new('Unsupported'),400 if OpenNebula::is_error?(rc) + + xml_hash = {'EBS' => { + 'INSTANCE_ID' => params['InstanceId'], + "DEVICE" => params['Device']} + } + + image.add_element('TEMPLATE', xml_hash) + rc = image.update + if OpenNebula::is_error?(rc) + logger.error rc.message + return OpenNebula::Error.new('Unsupported'),400 + end + + + # Response + erb_version = params['Version'] vm.info diff --git a/src/cloud/ec2/lib/views/describe_volumes.erb b/src/cloud/ec2/lib/views/describe_volumes.erb index 539059d3b5..aa92d12974 100644 --- a/src/cloud/ec2/lib/views/describe_volumes.erb +++ b/src/cloud/ec2/lib/views/describe_volumes.erb @@ -11,7 +11,16 @@ <%= state_image %> <%= im.render_create_time %> - + + <% im.each("TEMPLATE/EBS") do |ebs| %> + + vol-<%= sprintf('%08i', im.id) %> + <%= ebs['INSTANCE_ID'] %> + <%= ebs['DEVICE'] %> + attached + + <% end %> + <% else diff --git a/src/oca/ruby/OpenNebula/Image.rb b/src/oca/ruby/OpenNebula/Image.rb index ccac60026d..75976036ca 100644 --- a/src/oca/ruby/OpenNebula/Image.rb +++ b/src/oca/ruby/OpenNebula/Image.rb @@ -106,8 +106,9 @@ module OpenNebula # Replaces the template contents # - # +new_template+ New template contents - def update(new_template) + # +new_template+ New template contents. If no argument is provided + # the object will be updated using the @xml variable + def update(new_template=nil) super(IMAGE_METHODS[:update], new_template) end @@ -265,7 +266,7 @@ module OpenNebula chmod(-1, -1, -1, group_u, -1, -1, -1, -1, -1) end - + def set_persistent(persistence) return Error.new('ID not defined') if !@pe_id diff --git a/src/oca/ruby/OpenNebula/VirtualNetwork.rb b/src/oca/ruby/OpenNebula/VirtualNetwork.rb index f5c61c38f6..bb5c0933b3 100644 --- a/src/oca/ruby/OpenNebula/VirtualNetwork.rb +++ b/src/oca/ruby/OpenNebula/VirtualNetwork.rb @@ -88,7 +88,8 @@ module OpenNebula # Replaces the template contents # - # +new_template+ New template contents + # +new_template+ New template contents. If no argument is provided + # the object will be updated using the @xml variable def update(new_template=nil) super(VN_METHODS[:update], new_template) end From 50d0ff8f3559b8f4f8ef267e627c68091f60c5f9 Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Thu, 19 Jul 2012 11:59:25 +0200 Subject: [PATCH 05/13] feature #378: Improve ec2 error messages --- src/cloud/ec2/lib/EC2QueryServer.rb | 41 ++++++++++---- src/cloud/ec2/lib/ebs.rb | 28 +++++----- src/cloud/ec2/lib/econe-server.rb | 47 ++++++---------- src/cloud/ec2/lib/elastic_ip.rb | 83 ++++++++++++++++++----------- 4 files changed, 113 insertions(+), 86 deletions(-) diff --git a/src/cloud/ec2/lib/EC2QueryServer.rb b/src/cloud/ec2/lib/EC2QueryServer.rb index b0de206b6b..76a5dd9b12 100644 --- a/src/cloud/ec2/lib/EC2QueryServer.rb +++ b/src/cloud/ec2/lib/EC2QueryServer.rb @@ -23,6 +23,20 @@ require 'CloudServer' require 'ImageEC2' +module OpenNebula + class Error + attr_accessor :ec2_code + + def to_ec2 + xml = "" + xml << @ec2_code||"Unsupported" + xml << "" + xml << @message + xml << "" + end + end +end + ############################################################################### # The EC2Query Server implements a EC2 compatible server based on the # OpenNebula Engine @@ -90,12 +104,12 @@ class EC2QueryServer < CloudServer template = image.to_one_template if OpenNebula.is_error?(template) - return OpenNebula::Error.new('Unsupported'), 400 + return template end rc = image.allocate(template, @config[:datastore_id]||1) if OpenNebula.is_error?(rc) - return OpenNebula::Error.new('Unsupported'), 400 + return rc end erb_version = params['Version'] @@ -113,7 +127,8 @@ class EC2QueryServer < CloudServer # Enable the new Image rc = image.info if OpenNebula.is_error?(rc) - return OpenNebula::Error.new('InvalidAMIID.NotFound'), 400 + rc.ec2_code = "InvalidAMIID.NotFound" + return rc end image.enable @@ -169,7 +184,7 @@ class EC2QueryServer < CloudServer rc = vm.allocate(template_text) if OpenNebula::is_error?(rc) - return OpenNebula::Error.new('Unsupported'),400 + return rc end vm.info @@ -205,7 +220,9 @@ class EC2QueryServer < CloudServer vm = VirtualMachine.new(VirtualMachine.build_xml(vmid),@client) rc = vm.info - return OpenNebula::Error.new('Unsupported'),400 if OpenNebula::is_error?(rc) + if OpenNebula::is_error?(rc) + return rc + end if vm.status == 'runn' rc = vm.shutdown @@ -213,7 +230,9 @@ class EC2QueryServer < CloudServer rc = vm.finalize end - return OpenNebula::Error.new('Unsupported'),400 if OpenNebula::is_error?(rc) + if OpenNebula::is_error?(rc) + return rc + end erb_version = params['Version'] @@ -225,23 +244,23 @@ class EC2QueryServer < CloudServer # Elastic IP ########################################################################### def allocate_address(params) - return OpenNebula::Error.new('Unsupported'),400 + return OpenNebula::Error.new('Unsupported') end def release_address(params) - return OpenNebula::Error.new('Unsupported'),400 + return OpenNebula::Error.new('Unsupported') end def describe_addresses(params) - return OpenNebula::Error.new('Unsupported'),400 + return OpenNebula::Error.new('Unsupported') end def associate_address(params) - return OpenNebula::Error.new('Unsupported'),400 + return OpenNebula::Error.new('Unsupported') end def disassociate_address(params) - return OpenNebula::Error.new('Unsupported'),400 + return OpenNebula::Error.new('Unsupported') end ########################################################################### diff --git a/src/cloud/ec2/lib/ebs.rb b/src/cloud/ec2/lib/ebs.rb index debb91f1ad..a86ee5a65f 100644 --- a/src/cloud/ec2/lib/ebs.rb +++ b/src/cloud/ec2/lib/ebs.rb @@ -24,17 +24,21 @@ module EBS vm = VirtualMachine.new(VirtualMachine.build_xml(vm_id), @client) rc = vm.info - return OpenNebula::Error.new('Unsupported'),400 if OpenNebula::is_error?(rc) + return rc if OpenNebula::is_error?(rc) disk_id = vm["TEMPLATE/DISK[IMAGE_ID=#{image_id.to_i}]/DISK_ID"] logger.debug { "Detaching DISK: #{disk_id} VM: #{vm_id} IMAGE: #{image_id}" } - return OpenNebula::Error.new('Unsupported'),400 if disk_id.nil? + if disk_id.nil? + rc = OpenNebula::Error.new("There is no disk to be detached") + logger.error {rc.message} + return rc + end rc = vm.detachdisk(disk_id.to_i) - return OpenNebula::Error.new('Unsupported'),400 if OpenNebula::is_error?(rc) + return rc if OpenNebula::is_error?(rc) # Update IMAGE metadata @@ -42,13 +46,13 @@ module EBS image = Image.new(Image.build_xml(image_id), @client) rc = image.info - return OpenNebula::Error.new('Unsupported'),400 if OpenNebula::is_error?(rc) + return rc if OpenNebula::is_error?(rc) image.delete_element("TEMPLATE/EBS[INSTANCE_ID=\"#{params['InstanceId']}\"]") rc = image.update if OpenNebula::is_error?(rc) logger.error {rc.message} - return OpenNebula::Error.new('Unsupported'),400 + return rc end @@ -87,12 +91,12 @@ module EBS vm = VirtualMachine.new(VirtualMachine.build_xml(vm_id), @client) rc = vm.info - return OpenNebula::Error.new('Unsupported'),400 if OpenNebula::is_error?(rc) + return rc if OpenNebula::is_error?(rc) template = "DISK = [ IMAGE_ID = #{image_id}, TARGET = #{target} ]" vm.attachdisk(template) - return OpenNebula::Error.new('Unsupported'),400 if OpenNebula::is_error?(rc) + return rc if OpenNebula::is_error?(rc) # Update IMAGE metadata @@ -100,7 +104,7 @@ module EBS image = Image.new(Image.build_xml(image_id), @client) rc = image.info - return OpenNebula::Error.new('Unsupported'),400 if OpenNebula::is_error?(rc) + return rc if OpenNebula::is_error?(rc) xml_hash = {'EBS' => { 'INSTANCE_ID' => params['InstanceId'], @@ -111,7 +115,7 @@ module EBS rc = image.update if OpenNebula::is_error?(rc) logger.error rc.message - return OpenNebula::Error.new('Unsupported'),400 + return rc end @@ -148,12 +152,12 @@ module EBS template = image.to_one_template if OpenNebula.is_error?(template) - return OpenNebula::Error.new('Unsupported'), 400 + return template end rc = image.allocate(template, @config[:datastore_id]||1) if OpenNebula.is_error?(rc) - return OpenNebula::Error.new('Unsupported'), 400 + return rc end erb_version = params['Version'] @@ -175,7 +179,7 @@ module EBS image = ImageEC2.new(Image.build_xml(image_id), @client) rc = image.delete - return OpenNebula::Error.new('Unsupported'),400 if OpenNebula::is_error?(rc) + return rc if OpenNebula::is_error?(rc) erb_version = params['Version'] diff --git a/src/cloud/ec2/lib/econe-server.rb b/src/cloud/ec2/lib/econe-server.rb index cf57486939..b4faadab62 100644 --- a/src/cloud/ec2/lib/econe-server.rb +++ b/src/cloud/ec2/lib/econe-server.rb @@ -136,42 +136,23 @@ before do username = settings.cloud_auth.auth(request.env, params) rescue Exception => e logger.error {e.message} - error 500, error_xml("AuthFailure", 0) + rc = OpenNebula::Error.new(e.message) + rc.ec2_code = "AuthFailure" + error 401, rc.to_ec2 end if username.nil? - error 401, error_xml("AuthFailure", 0) + rc = OpenNebula::Error.new("The username or password is not correct") + rc.ec2_code = "AuthFailure" + error 401, rc.to_ec2 else client = settings.cloud_auth.client(username) oneadmin_client = settings.cloud_auth.client - @econe_server = EC2QueryServer.new(client, oneadmin_client, settings.config, settings.logger) - end -end - -helpers do - def error_xml(code,id) - message = '' - - case code - when 'AuthFailure' - message = 'User not authorized' - when 'InvalidAMIID.NotFound' - message = 'Specified AMI ID does not exist' - when 'Unsupported' - message = 'The instance type or feature is not supported in your requested Availability Zone.' - else - message = code - end - - xml = ""+ - code + - "" + - message + - "" + - id.to_s + - "" - - return xml + @econe_server = EC2QueryServer.new( + client, + oneadmin_client, + settings.config, + settings.logger) end end @@ -221,10 +202,14 @@ def do_http_request(params) if OpenNebula::is_error?(result) logger.error(result.message) - error rc, error_xml(result.message, 0) + error CloudServer::HTTP_ERROR_CODE[result.errno], result.to_ec2 end headers['Content-Type'] = 'application/xml' + if rc + status rc + end + result end diff --git a/src/cloud/ec2/lib/elastic_ip.rb b/src/cloud/ec2/lib/elastic_ip.rb index 0a93d34120..a495a8c9a6 100644 --- a/src/cloud/ec2/lib/elastic_ip.rb +++ b/src/cloud/ec2/lib/elastic_ip.rb @@ -2,12 +2,16 @@ module ElasticIP def allocate_address(params) # Get public IP vnet = retrieve_eip_vnet - return vnet, 400 if OpenNebula::is_error?(vnet) + if OpenNebula::is_error?(vnet) + return vnet + end ips = vnet.retrieve_elements('LEASES/LEASE[USED=0]/IP') if ips.nil? - logger.error { "There is no lease available to be allocated" } - return OpenNebula::Error.new('AddressLimitExceeded'), 400 + rc = OpenNebula::Error.new("There is no lease available to be allocated") + rc.ec2_code = "AddressLimitExceeded" + logger.error { rc.message } + return rc end eip = ips.first @@ -16,7 +20,7 @@ module ElasticIP rc = vnet.hold(eip) if OpenNebula::is_error?(rc) logger.error rc.message - return OpenNebula::Error.new('Unsupported'),400 + return rc end # Update EC2_ADDRESSES list @@ -25,7 +29,7 @@ module ElasticIP rc = vnet.update if OpenNebula::is_error?(rc) logger.error rc.message - return OpenNebula::Error.new('Unsupported'),400 + return rc end response = ERB.new(File.read(@config[:views]+"/allocate_address.erb")) @@ -35,20 +39,24 @@ module ElasticIP def release_address(params) # Check public IP vnet = retrieve_eip_vnet - return vnet, 400 if OpenNebula::is_error?(vnet) + if OpenNebula::is_error?(vnet) + return vnet + end eip = params["PublicIp"] unless vnet["TEMPLATE/EC2_ADDRESSES[IP=\"#{eip}\" and UID=\"#{retrieve_uid}\"]/IP"] - logger.error { "address:#{eip} does not exist" } - return OpenNebula::Error.new('Unsupported'),400 + rc = OpenNebula::Error.new("address:#{eip} does not exist") + logger.error { rc.message } + return rc end # Disassociate address if needed if vnet["TEMPLATE/EC2_ADDRESSES[IP=\"#{eip}\"]/VMID"] cmd_output = `#{@config[:disassociate_script]} #{eip}` if $?.to_i != 0 - logger.error { cmd_output } - return OpenNebula::Error.new('Unsupported'),400 + rc = OpenNebula::Error.new(cmd_output) + logger.error { rc.message } + return rc end vnet.delete_element("TEMPLATE/EC2_ADDRESSES[IP=\"#{eip}\"]/VMID") @@ -58,7 +66,7 @@ module ElasticIP rc = vnet.release(eip) if OpenNebula::is_error?(rc) logger.error {rc.message} - return OpenNebula::Error.new('Unsupported'),400 + return rc end # Update EC2_ADDRESSES list @@ -66,7 +74,7 @@ module ElasticIP rc = vnet.update if OpenNebula::is_error?(rc) logger.error {rc.message} - return OpenNebula::Error.new('Unsupported'),400 + return rc end response = ERB.new(File.read(@config[:views]+"/release_address.erb")) @@ -75,7 +83,9 @@ module ElasticIP def describe_addresses(params) vnet = retrieve_eip_vnet - return vnet, 400 if OpenNebula::is_error?(vnet) + if OpenNebula::is_error?(vnet) + return vnet + end erb_version = params['Version'] user_id = retrieve_uid @@ -87,7 +97,9 @@ module ElasticIP def associate_address(params) # Check public IP vnet = retrieve_eip_vnet - return vnet, 400 if OpenNebula::is_error?(vnet) + if OpenNebula::is_error?(vnet) + return vnet + end user_id = retrieve_uid eip = params["PublicIp"] @@ -95,8 +107,9 @@ module ElasticIP vmid = vmid.split('-')[1] if vmid[0]==?i unless vnet["TEMPLATE/EC2_ADDRESSES[IP=\"#{eip}\" and UID=\"#{retrieve_uid}\"]/IP"] - logger.error { "address:#{eip} does not exist" } - return OpenNebula::Error.new('Unsupported'),400 + rc = OpenNebula::Error.new("address:#{eip} does not exist") + logger.error { rc.message } + return rc end # Get private IP of the Instance @@ -104,13 +117,14 @@ module ElasticIP rc = vm.info if OpenNebula::is_error?(rc) logger.error {rc.message} - return OpenNebula::Error.new('Unsupported'),400 + return rc end ips = vm.retrieve_elements('TEMPLATE/NIC/IP') if ips.nil? - logger.error { "The instance does not have any NIC" } - return OpenNebula::Error.new('Unsupported'),400 + rc = OpenNebula::Error.new("The instance does not have any NIC") + logger.error { rc.message } + return rc end private_ip = ips.first @@ -119,8 +133,9 @@ module ElasticIP if vnet["TEMPLATE/EC2_ADDRESSES[IP=\"#{eip}\"]/VMID"] cmd_output = `#{@config[:disassociate_script]} #{eip}` if $?.to_i != 0 - logger.error { cmd_output } - return OpenNebula::Error.new('Unsupported'),400 + rc = OpenNebula::Error.new(cmd_output) + logger.error { rc.message } + return rc end vnet.delete_element("TEMPLATE/EC2_ADDRESSES[IP=\"#{eip}\"]/VMID") @@ -130,8 +145,9 @@ module ElasticIP vnet_base64 = Base64.encode64(vnet.to_xml).delete("\n") cmd_output = `#{@config[:associate_script]} #{eip} #{private_ip} \"#{vnet_base64}\"` if $?.to_i != 0 - logger.error { "associate_script" << cmd_output } - return OpenNebula::Error.new('Unsupported'),400 + rc = OpenNebula::Error.new(cmd_output) + logger.error { rc.message } + return rc end # Update EC2_ADDRESSES list @@ -139,7 +155,7 @@ module ElasticIP rc = vnet.update if OpenNebula::is_error?(rc) logger.error {rc.message} - return OpenNebula::Error.new('Unsupported'),400 + return rc end response = ERB.new(File.read(@config[:views]+"/associate_address.erb")) @@ -149,19 +165,23 @@ module ElasticIP def disassociate_address(params) # Check public IP vnet = retrieve_eip_vnet - return vnet, 400 if OpenNebula::is_error?(vnet) + if OpenNebula::is_error?(vnet) + return vnet + end eip = params["PublicIp"] unless vnet["TEMPLATE/EC2_ADDRESSES[IP=\"#{eip}\" and UID=\"#{retrieve_uid}\"]/VMID"] - logger.error { "address:#{eip} does not exist or is not associated with any instance" } - return OpenNebula::Error.new('Unsupported'),400 + rc = OpenNebula::Error.new("address:#{eip} does not exist or is not associated with any instance") + logger.error { rc.message } + return rc end # Run external script cmd_output = `#{@config[:disassociate_script]} #{eip}` if $?.to_i != 0 - logger.error { cmd_output } - return OpenNebula::Error.new('Unsupported'),400 + rc = OpenNebula::Error.new(cmd_output) + logger.error { rc.message } + return rc end # Update EC2_ADDRESSES list @@ -169,7 +189,7 @@ module ElasticIP rc = vnet.update if OpenNebula::is_error?(rc) logger.error {rc.message} - return OpenNebula::Error.new('Unsupported'),400 + return rc end response = ERB.new(File.read(@config[:views]+"/disassociate_address.erb")) @@ -183,8 +203,7 @@ module ElasticIP rc = vnet.info if OpenNebula::is_error?(rc) - logger.error {rc.message} - return OpenNebula::Error.new('Unsupported') + return rc end vnet From 8cdc24e77e66384c616b6b7188809af38a5385c0 Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Mon, 13 Aug 2012 18:27:54 +0200 Subject: [PATCH 06/13] feature #378: refactor instance functionality and add new ec2 commands: * StartInstances * StopInstances * RebootInstances * DescribeRegions * DescribeAvailabilityZones --- install.sh | 14 +- src/cloud/ec2/bin/econe-reboot-instances | 125 +++++++++++++ src/cloud/ec2/bin/econe-start-instances | 126 +++++++++++++ src/cloud/ec2/bin/econe-stop-instances | 126 +++++++++++++ src/cloud/ec2/lib/EC2QueryClient.rb | 51 ++++++ src/cloud/ec2/lib/EC2QueryServer.rb | 157 +++------------- src/cloud/ec2/lib/econe-server.rb | 10 ++ src/cloud/ec2/lib/instance.rb | 169 ++++++++++++++++++ .../lib/views/describe_availability_zones.erb | 12 ++ .../ec2/lib/views/describe_instances.erb | 52 +++--- src/cloud/ec2/lib/views/describe_regions.erb | 10 ++ src/cloud/ec2/lib/views/reboot_instances.erb | 5 + src/cloud/ec2/lib/views/run_instances.erb | 61 ++++--- src/cloud/ec2/lib/views/start_instances.erb | 13 ++ src/cloud/ec2/lib/views/stop_instances.erb | 13 ++ .../ec2/lib/views/terminate_instances.erb | 23 ++- 16 files changed, 766 insertions(+), 201 deletions(-) create mode 100755 src/cloud/ec2/bin/econe-reboot-instances create mode 100755 src/cloud/ec2/bin/econe-start-instances create mode 100755 src/cloud/ec2/bin/econe-stop-instances create mode 100644 src/cloud/ec2/lib/instance.rb create mode 100644 src/cloud/ec2/lib/views/describe_availability_zones.erb create mode 100644 src/cloud/ec2/lib/views/describe_regions.erb create mode 100644 src/cloud/ec2/lib/views/reboot_instances.erb create mode 100644 src/cloud/ec2/lib/views/start_instances.erb create mode 100644 src/cloud/ec2/lib/views/stop_instances.erb diff --git a/install.sh b/install.sh index d70daf408e..bdbb7f2d39 100755 --- a/install.sh +++ b/install.sh @@ -1072,12 +1072,15 @@ ECO_LIB_FILES="src/cloud/ec2/lib/EC2QueryClient.rb \ src/cloud/ec2/lib/ImageEC2.rb \ src/cloud/ec2/lib/elastic_ip.rb \ src/cloud/ec2/lib/ebs.rb \ + src/cloud/ec2/lib/instance.rb \ src/cloud/ec2/lib/econe-server.rb" ECO_LIB_CLIENT_FILES="src/cloud/ec2/lib/EC2QueryClient.rb" ECO_LIB_VIEW_FILES="src/cloud/ec2/lib/views/describe_images.erb \ src/cloud/ec2/lib/views/describe_instances.erb \ + src/cloud/ec2/lib/views/describe_regions.erb \ + src/cloud/ec2/lib/views/describe_availability_zones.erb \ src/cloud/ec2/lib/views/create_volume.erb \ src/cloud/ec2/lib/views/describe_volumes.erb \ src/cloud/ec2/lib/views/attach_volume.erb \ @@ -1090,7 +1093,10 @@ ECO_LIB_VIEW_FILES="src/cloud/ec2/lib/views/describe_images.erb \ src/cloud/ec2/lib/views/disassociate_address.erb \ src/cloud/ec2/lib/views/describe_addresses.erb \ src/cloud/ec2/lib/views/release_address.erb \ - src/cloud/ec2/lib/views/terminate_instances.erb" + src/cloud/ec2/lib/views/terminate_instances.erb \ + src/cloud/ec2/lib/views/stop_instances.erb \ + src/cloud/ec2/lib/views/reboot_instances.erb \ + src/cloud/ec2/lib/views/start_instances.erb" ECO_BIN_FILES="src/cloud/ec2/bin/econe-server \ src/cloud/ec2/bin/econe-describe-images \ @@ -1103,6 +1109,9 @@ ECO_BIN_FILES="src/cloud/ec2/bin/econe-server \ src/cloud/ec2/bin/econe-create-volume \ src/cloud/ec2/bin/econe-run-instances \ src/cloud/ec2/bin/econe-terminate-instances \ + src/cloud/ec2/bin/econe-start-instances \ + src/cloud/ec2/bin/econe-stop-instances \ + src/cloud/ec2/bin/econe-reboot-instances \ src/cloud/ec2/bin/econe-describe-addresses \ src/cloud/ec2/bin/econe-allocate-address \ src/cloud/ec2/bin/econe-release-address \ @@ -1120,6 +1129,9 @@ ECO_BIN_CLIENT_FILES="src/cloud/ec2/bin/econe-describe-images \ src/cloud/ec2/bin/econe-create-volume \ src/cloud/ec2/bin/econe-run-instances \ src/cloud/ec2/bin/econe-terminate-instances \ + src/cloud/ec2/bin/econe-start-instances \ + src/cloud/ec2/bin/econe-stop-instances \ + src/cloud/ec2/bin/econe-reboot-instances \ src/cloud/ec2/bin/econe-describe-addresses \ src/cloud/ec2/bin/econe-allocate-address \ src/cloud/ec2/bin/econe-release-address \ diff --git a/src/cloud/ec2/bin/econe-reboot-instances b/src/cloud/ec2/bin/econe-reboot-instances new file mode 100755 index 0000000000..d264480249 --- /dev/null +++ b/src/cloud/ec2/bin/econe-reboot-instances @@ -0,0 +1,125 @@ +#!/usr/bin/env ruby +# -------------------------------------------------------------------------- # +# Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org) # +# # +# Licensed 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. # +#--------------------------------------------------------------------------- # + +ONE_LOCATION=ENV["ONE_LOCATION"] + +if !ONE_LOCATION + RUBY_LIB_LOCATION="/usr/lib/one/ruby" +else + RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby" +end + + +$: << RUBY_LIB_LOCATION +$: << RUBY_LIB_LOCATION+"/cloud" + +COMMANDS_HELP=<<-EOT +econe-reboot-instances + +reboot the selected running instance + +Usage: + econe-register [OPTIONS] INSTANCE-ID + +Options: + + --help, -h + Show help + + --access-key , -K + The username of the user + + --secret-key , -S + The password of the user + + --url , -U + Set url as the web service url to use + +INSTANCE-ID: The instance identification as returned by +the econe-run-instances command + +EOT + +require 'econe/EC2QueryClient' +require 'CloudClient' +require 'getoptlong' + +include CloudCLI + +opts = GetoptLong.new( + ['--help', '-h',GetoptLong::NO_ARGUMENT], + ['--version', '-v',GetoptLong::NO_ARGUMENT], + ['--access-key', '-K',GetoptLong::REQUIRED_ARGUMENT], + ['--secret-key', '-S',GetoptLong::REQUIRED_ARGUMENT], + ['--url', '-U',GetoptLong::REQUIRED_ARGUMENT] + ) + +url = nil +access = nil +secret = nil +auth = nil + +begin + opts.each do |opt, arg| + case opt + when '--help' + puts COMMANDS_HELP + return + when '--version' + puts CloudCLI.version_text + exit 0 + when '--access-key' + access = arg + when '--secret-key' + secret = arg + when '--url' + url = arg + when '--headers' + headers = true + end + end +rescue Exception => e + exit -1 +end + +instance = ARGV.shift + +if !instance + puts "#{cmd_name}: missing INSTANCE-ID parameter" + exit -1 +end + +auth = "#{access}:#{secret}" if secret && access + +begin + ec2_client = EC2QueryClient::Client.new(auth,url) +rescue Exception => e + puts "#{cmd_name}: #{e.message}" + exit -1 +end + +rc = ec2_client.reboot_instances(instance) + +if CloudClient::is_error?(rc) + puts "#{cmd_name}: #{rc.message}" + exit -1 +end + + +puts "Success: Reboting" + +exit 0 diff --git a/src/cloud/ec2/bin/econe-start-instances b/src/cloud/ec2/bin/econe-start-instances new file mode 100755 index 0000000000..e2dd0fdd8d --- /dev/null +++ b/src/cloud/ec2/bin/econe-start-instances @@ -0,0 +1,126 @@ +#!/usr/bin/env ruby +# -------------------------------------------------------------------------- # +# Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org) # +# # +# Licensed 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. # +#--------------------------------------------------------------------------- # + +ONE_LOCATION=ENV["ONE_LOCATION"] + +if !ONE_LOCATION + RUBY_LIB_LOCATION="/usr/lib/one/ruby" +else + RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby" +end + + +$: << RUBY_LIB_LOCATION +$: << RUBY_LIB_LOCATION+"/cloud" + +COMMANDS_HELP=<<-EOT +econe-start-instances + +Start the selected running instance + +Usage: + econe-register [OPTIONS] INSTANCE-ID + +Options: + + --help, -h + Show help + + --access-key , -K + The username of the user + + --secret-key , -S + The password of the user + + --url , -U + Set url as the web service url to use + +INSTANCE-ID: The instance identification as returned by +the econe-run-instances command + +EOT + +require 'econe/EC2QueryClient' +require 'CloudClient' +require 'getoptlong' + +include CloudCLI + +opts = GetoptLong.new( + ['--help', '-h',GetoptLong::NO_ARGUMENT], + ['--version', '-v',GetoptLong::NO_ARGUMENT], + ['--access-key', '-K',GetoptLong::REQUIRED_ARGUMENT], + ['--secret-key', '-S',GetoptLong::REQUIRED_ARGUMENT], + ['--url', '-U',GetoptLong::REQUIRED_ARGUMENT] + ) + +url = nil +access = nil +secret = nil +auth = nil + +begin + opts.each do |opt, arg| + case opt + when '--help' + puts COMMANDS_HELP + return + when '--version' + puts CloudCLI.version_text + exit 0 + when '--access-key' + access = arg + when '--secret-key' + secret = arg + when '--url' + url = arg + when '--headers' + headers = true + end + end +rescue Exception => e + exit -1 +end + +instance = ARGV.shift + +if !instance + puts "#{cmd_name}: missing INSTANCE-ID parameter" + exit -1 +end + +auth = "#{access}:#{secret}" if secret && access + +begin + ec2_client = EC2QueryClient::Client.new(auth,url) +rescue Exception => e + puts "#{cmd_name}: #{e.message}" + exit -1 +end + +rc = ec2_client.start_instances(instance) + +if CloudClient::is_error?(rc) + puts "#{cmd_name}: #{rc.message}" + exit -1 +end + +result = rc['instancesSet']['item'][0] + +puts "Success: Starting #{result['instanceId']} in #{result['previousState']['name']} state" + +exit 0 diff --git a/src/cloud/ec2/bin/econe-stop-instances b/src/cloud/ec2/bin/econe-stop-instances new file mode 100755 index 0000000000..d64afab566 --- /dev/null +++ b/src/cloud/ec2/bin/econe-stop-instances @@ -0,0 +1,126 @@ +#!/usr/bin/env ruby +# -------------------------------------------------------------------------- # +# Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org) # +# # +# Licensed 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. # +#--------------------------------------------------------------------------- # + +ONE_LOCATION=ENV["ONE_LOCATION"] + +if !ONE_LOCATION + RUBY_LIB_LOCATION="/usr/lib/one/ruby" +else + RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby" +end + + +$: << RUBY_LIB_LOCATION +$: << RUBY_LIB_LOCATION+"/cloud" + +COMMANDS_HELP=<<-EOT +econe-stop-instances + +Stop the selected running instance + +Usage: + econe-register [OPTIONS] INSTANCE-ID + +Options: + + --help, -h + Show help + + --access-key , -K + The username of the user + + --secret-key , -S + The password of the user + + --url , -U + Set url as the web service url to use + +INSTANCE-ID: The instance identification as returned by +the econe-run-instances command + +EOT + +require 'econe/EC2QueryClient' +require 'CloudClient' +require 'getoptlong' + +include CloudCLI + +opts = GetoptLong.new( + ['--help', '-h',GetoptLong::NO_ARGUMENT], + ['--version', '-v',GetoptLong::NO_ARGUMENT], + ['--access-key', '-K',GetoptLong::REQUIRED_ARGUMENT], + ['--secret-key', '-S',GetoptLong::REQUIRED_ARGUMENT], + ['--url', '-U',GetoptLong::REQUIRED_ARGUMENT] + ) + +url = nil +access = nil +secret = nil +auth = nil + +begin + opts.each do |opt, arg| + case opt + when '--help' + puts COMMANDS_HELP + return + when '--version' + puts CloudCLI.version_text + exit 0 + when '--access-key' + access = arg + when '--secret-key' + secret = arg + when '--url' + url = arg + when '--headers' + headers = true + end + end +rescue Exception => e + exit -1 +end + +instance = ARGV.shift + +if !instance + puts "#{cmd_name}: missing INSTANCE-ID parameter" + exit -1 +end + +auth = "#{access}:#{secret}" if secret && access + +begin + ec2_client = EC2QueryClient::Client.new(auth,url) +rescue Exception => e + puts "#{cmd_name}: #{e.message}" + exit -1 +end + +rc = ec2_client.stop_instances(instance) + +if CloudClient::is_error?(rc) + puts "#{cmd_name}: #{rc.message}" + exit -1 +end + +result = rc['instancesSet']['item'][0] + +puts "Success: Stopping #{result['instanceId']} in #{result['previousState']['name']} state" + +exit 0 diff --git a/src/cloud/ec2/lib/EC2QueryClient.rb b/src/cloud/ec2/lib/EC2QueryClient.rb index 32170f10fc..03a92c2768 100644 --- a/src/cloud/ec2/lib/EC2QueryClient.rb +++ b/src/cloud/ec2/lib/EC2QueryClient.rb @@ -135,6 +135,57 @@ module EC2QueryClient return response end + ###################################################################### + # + # + ###################################################################### + def stop_instances(instance_id) + begin + response = @ec2_connection.stop_instances( + :instance_id => instance_id + ) + rescue Exception => e + error = CloudClient::Error.new(e.message) + return error + end + + return response + end + + ###################################################################### + # + # + ###################################################################### + def start_instances(instance_id) + begin + response = @ec2_connection.start_instances( + :instance_id => instance_id + ) + rescue Exception => e + error = CloudClient::Error.new(e.message) + return error + end + + return response + end + + ###################################################################### + # + # + ###################################################################### + def reboot_instances(instance_id) + begin + response = @ec2_connection.reboot_instances( + :instance_id => instance_id + ) + rescue Exception => e + error = CloudClient::Error.new(e.message) + return error + end + + return response + end + ###################################################################### # # Returns true if HTTP code is 200, diff --git a/src/cloud/ec2/lib/EC2QueryServer.rb b/src/cloud/ec2/lib/EC2QueryServer.rb index 76a5dd9b12..2cd48f7fd3 100644 --- a/src/cloud/ec2/lib/EC2QueryServer.rb +++ b/src/cloud/ec2/lib/EC2QueryServer.rb @@ -22,6 +22,9 @@ require 'base64' require 'CloudServer' require 'ImageEC2' +require 'ebs' +require 'elastic_ip' +require 'instance' module OpenNebula class Error @@ -29,7 +32,7 @@ module OpenNebula def to_ec2 xml = "" - xml << @ec2_code||"Unsupported" + xml << (@ec2_code||"Unsupported") xml << "" xml << @message xml << "" @@ -43,36 +46,6 @@ end ############################################################################### class EC2QueryServer < CloudServer - ########################################################################### - # Class Constants. Defined the EC2 and OpenNebula State mapping - ########################################################################### - EC2_STATES={ - :pending => {:code => 0, :name => 'pending'}, - :running => {:code => 16,:name => 'running'}, - :shutdown => {:code => 32,:name => 'shutting-down'}, - :terminated => {:code => 48,:name => 'terminated'} - } - - ONE_STATES={ - 'init' => :pending, - 'pend' => :pending, - 'hold' => :pending, - 'stop' => :pending, - 'susp' => :pending, - 'done' => :terminated, - 'fail' => :terminated, - 'prol' => :pending, - 'boot' => :running, - 'runn' => :running, - 'migr' => :running, - 'save' => :pending, - 'epil' => :shutdown, - 'shut' => :shutdown, - 'clea' => :shutdown, - 'fail' => :terminated, - 'unkn' => :terminated - } - ########################################################################### def initialize(client, oneadmin_client, config, logger) @@ -81,15 +54,35 @@ class EC2QueryServer < CloudServer @client = client @oneadmin_client = oneadmin_client + if config[:ssl_server] + @base_url=config[:ssl_server] + else + @base_url="http://#{config[:server]}:#{config[:port]}" + end + if @config[:elasticips_vnet_id].nil? logger.error { 'ElasticIP module not loaded' } else - require 'elastic_ip' extend ElasticIP end - require 'ebs' extend EBS + extend Instance + end + + ########################################################################### + # Regions and Availability Zones + ########################################################################### + + def describe_availability_zones(params) + response = ERB.new( + File.read(@config[:views]+"/describe_availability_zones.erb")) + return response.result(binding), 200 + end + + def describe_regions(params) + response = ERB.new(File.read(@config[:views]+"/describe_regions.erb")) + return response.result(binding), 200 end ########################################################################### @@ -150,95 +143,6 @@ class EC2QueryServer < CloudServer return response.result(binding), 200 end - ########################################################################### - # Instance Interface - ########################################################################### - - def run_instances(params) - # Get the instance type and path - if params['InstanceType'] != nil - instance_type_name = params['InstanceType'] - instance_type = @config[:instance_types][instance_type_name.to_sym] - - if instance_type != nil - path = @config[:template_location] + "/#{instance_type[:template]}" - end - end - - # Get the image - tmp, img=params['ImageId'].split('-') - - # Build the VM - erb_vm_info=Hash.new - erb_vm_info[:img_id] = img.to_i - erb_vm_info[:ec2_img_id] = params['ImageId'] - erb_vm_info[:instance_type] = instance_type_name - erb_vm_info[:template] = path - erb_vm_info[:user_data] = params['UserData'] - - template = ERB.new(File.read(erb_vm_info[:template])) - template_text = template.result(binding) - - # Start the VM. - vm = VirtualMachine.new(VirtualMachine.build_xml, @client) - - rc = vm.allocate(template_text) - if OpenNebula::is_error?(rc) - return rc - end - - vm.info - - erb_vm_info[:vm_id]=vm.id - erb_vm_info[:vm]=vm - erb_user_name = params['AWSAccessKeyId'] - erb_version = params['Version'] - - response = ERB.new(File.read(@config[:views]+"/run_instances.erb")) - return response.result(binding), 200 - end - - def describe_instances(params) - user_flag = OpenNebula::Pool::INFO_ALL - vmpool = VirtualMachinePool.new(@client, user_flag) - vmpool.info - - erb_version = params['Version'] - erb_user_name = params['AWSAccessKeyId'] - - response = ERB.new(File.read(@config[:views]+"/describe_instances.erb")) - return response.result(binding), 200 - end - - def terminate_instances(params) - # Get the VM ID - vmid=params['InstanceId.1'] - vmid=params['InstanceId.01'] if !vmid - - tmp, vmid=vmid.split('-') if vmid[0]==?i - - vm = VirtualMachine.new(VirtualMachine.build_xml(vmid),@client) - rc = vm.info - - if OpenNebula::is_error?(rc) - return rc - end - - if vm.status == 'runn' - rc = vm.shutdown - else - rc = vm.finalize - end - - if OpenNebula::is_error?(rc) - return rc - end - - erb_version = params['Version'] - - response =ERB.new(File.read(@config[:views]+"/terminate_instances.erb")) - return response.result(binding), 200 - end ########################################################################### # Elastic IP @@ -268,15 +172,6 @@ class EC2QueryServer < CloudServer ########################################################################### private - def render_state(vm) - one_state = ONE_STATES[vm.status] - ec2_state = EC2_STATES[one_state||:pending] - - return "#{ec2_state[:code]} - #{ec2_state[:name]}" - end - - def render_launch_time(vm) return "#{Time.at(vm["STIME"].to_i).xmlschema}" diff --git a/src/cloud/ec2/lib/econe-server.rb b/src/cloud/ec2/lib/econe-server.rb index b4faadab62..af82e75f31 100644 --- a/src/cloud/ec2/lib/econe-server.rb +++ b/src/cloud/ec2/lib/econe-server.rb @@ -178,6 +178,12 @@ def do_http_request(params) result,rc = @econe_server.describe_instances(params) when 'TerminateInstances' result,rc = @econe_server.terminate_instances(params) + when 'StartInstances' + result,rc = @econe_server.start_instances(params) + when 'StopInstances' + result,rc = @econe_server.stop_instances(params) + when 'RebootInstances' + result,rc = @econe_server.reboot_instances(params) when 'AllocateAddress' result,rc = @econe_server.allocate_address(params) when 'AssociateAddress' @@ -188,6 +194,10 @@ def do_http_request(params) result,rc = @econe_server.release_address(params) when 'DescribeAddresses' result,rc = @econe_server.describe_addresses(params) + when 'DescribeRegions' + result,rc = @econe_server.describe_regions(params) + when 'DescribeAvailabilityZones' + result,rc = @econe_server.describe_availability_zones(params) when 'CreateVolume' result,rc = @econe_server.create_volume(params) when 'DescribeVolumes' diff --git a/src/cloud/ec2/lib/instance.rb b/src/cloud/ec2/lib/instance.rb new file mode 100644 index 0000000000..b263982003 --- /dev/null +++ b/src/cloud/ec2/lib/instance.rb @@ -0,0 +1,169 @@ +module Instance + ########################################################################### + # Class Constants. Defined the EC2 and OpenNebula State mapping + ########################################################################### + EC2_STATES={ + :pending => {:code => 0, :name => 'pending'}, + :running => {:code => 16,:name => 'running'}, + :shutdown => {:code => 32,:name => 'shutting-down'}, + :terminated => {:code => 48,:name => 'terminated'}, + :stopping => {:code => 64,:name => 'stopping'}, + :stopped => {:code => 80,:name => 'stopped'} + } + + ONE_STATES={ + 'init' => :pending, + 'pend' => :pending, + 'hold' => :pending, + 'stop' => :stopped, + 'susp' => :stopped, + 'done' => :terminated, + 'fail' => :terminated, + 'prol' => :pending, + 'boot' => :running, + 'runn' => :running, + 'migr' => :running, + 'save' => :stopping, + 'epil' => :shutdown, + 'shut' => :shutdown, + 'clea' => :shutdown, + 'fail' => :terminated, + 'unkn' => :terminated + } + + def run_instances(params) + # Get the instance type and path + if params['InstanceType'] != nil + instance_type_name = params['InstanceType'] + instance_type = @config[:instance_types][instance_type_name.to_sym] + + if instance_type != nil + path = @config[:template_location] + "/#{instance_type[:template]}" + end + end + + # Get the image + tmp, img=params['ImageId'].split('-') + + # Build the VM + erb_vm_info=Hash.new + erb_vm_info[:img_id] = img.to_i + erb_vm_info[:ec2_img_id] = params['ImageId'] + erb_vm_info[:instance_type] = instance_type_name + erb_vm_info[:template] = path + erb_vm_info[:user_data] = params['UserData'] + + template = ERB.new(File.read(erb_vm_info[:template])) + template_text = template.result(binding) + + # Start the VM. + vm = VirtualMachine.new(VirtualMachine.build_xml, @client) + + rc = vm.allocate(template_text) + if OpenNebula::is_error?(rc) + return rc + end + + vm.info + + erb_current_state = render_state(vm) + erb_instance_id = render_instance_id(vm) + + erb_user_name = params['AWSAccessKeyId'] + erb_version = params['Version'] + + response = ERB.new(File.read(@config[:views]+"/run_instances.erb")) + return response.result(binding), 200 + end + + def describe_instances(params) + user_flag = OpenNebula::Pool::INFO_ALL + vmpool = VirtualMachinePool.new(@client, user_flag) + vmpool.info + + erb_version = params['Version'] + erb_user_name = params['AWSAccessKeyId'] + + response = ERB.new(File.read(@config[:views]+"/describe_instances.erb")) + return response.result(binding), 200 + end + + def terminate_instances(params) + perform_action(params, "terminate_instances.erb") { |vm| + if vm.status == 'runn' + vm.shutdown + else + vm.finalize + end + } + end + + def start_instances(params) + perform_action(params, "start_instances.erb") { |vm| + vm.resume + } + end + + def stop_instances(params) + perform_action(params, "stop_instances.erb") { |vm| + vm.stop + } + end + + def reboot_instances(params) + perform_action(params, "reboot_instances.erb") { |vm| + vm.reboot + } + end + + private + + # Perform an action on a given vm + # @param [Hash] params + # @option params [String] InstanceId The ID of the VM + # @param [String] erb_name name of the file, inside the views folder, + # to generate the response + # @yieldparam [OpenNebula::VirtualMachine] vm the VM + # @yieldreturn [OpenNebula::Error, nil] + # @return [OpenNebula::Error, nil] + def perform_action(params, erb_name, &block) + # Get the VM ID + vmid=params['InstanceId.1'] + vmid=params['InstanceId.01'] if !vmid + + tmp, vmid=vmid.split('-') if vmid[0]==?i + + vm = VirtualMachine.new(VirtualMachine.build_xml(vmid),@client) + + rc = vm.info + if OpenNebula::is_error?(rc) + return rc + end + + erb_previous_state = render_state(vm) + + rc = block.call(vm) + + vm.info + + erb_current_state = render_state(vm) + erb_instance_id = render_instance_id(vm) + + erb_version = params['Version'] + + response =ERB.new(File.read(@config[:views]+'/'+erb_name)) + return response.result(binding), 200 + end + + def render_state(vm) + one_state = ONE_STATES[vm.status] + ec2_state = EC2_STATES[one_state||:pending] + + return "#{ec2_state[:code]}#{ec2_state[:name]}" + end + + def render_instance_id(vm) + instance_id = "i-" + sprintf('%08i', vm.id) + return "#{instance_id}" + end +end \ No newline at end of file diff --git a/src/cloud/ec2/lib/views/describe_availability_zones.erb b/src/cloud/ec2/lib/views/describe_availability_zones.erb new file mode 100644 index 0000000000..d7798980be --- /dev/null +++ b/src/cloud/ec2/lib/views/describe_availability_zones.erb @@ -0,0 +1,12 @@ + + + + + + opennebula + available + opennebula + + + + \ No newline at end of file diff --git a/src/cloud/ec2/lib/views/describe_instances.erb b/src/cloud/ec2/lib/views/describe_instances.erb index 09fae94ae5..3718770fbb 100644 --- a/src/cloud/ec2/lib/views/describe_instances.erb +++ b/src/cloud/ec2/lib/views/describe_instances.erb @@ -1,41 +1,41 @@ - + 4ac62eaf-e266-4058-a970-2c01568cd417 - - - default - <%= erb_user_name %> - - - default - - - + + + default + <%= erb_user_name %> + + + default + + + <% vmpool.each do |vm| %> - - i-<%= vm.id %> - <%= vm['TEMPLATE/IMAGE_ID'] %> - + + <%= render_instance_id(vm) %> + <%= vm['TEMPLATE/IMAGE_ID'] %> + <%= render_state(vm) %> - - <%= vm["TEMPLATE/NIC/IP"] %> - <%= vm["TEMPLATE/NIC/IP"] %> - default - 0 + + <%= vm["TEMPLATE/NIC/IP"] %> + <%= vm["TEMPLATE/NIC/IP"] %> + default + 0 - <%= vm['TEMPLATE/INSTANCE_TYPE'] %> + <%= vm['TEMPLATE/INSTANCE_TYPE'] %> <%= render_launch_time(vm) %> - - default - - + + default + + eki-EA801065 eri-1FEE1144 false - + <% end %> diff --git a/src/cloud/ec2/lib/views/describe_regions.erb b/src/cloud/ec2/lib/views/describe_regions.erb new file mode 100644 index 0000000000..388180cf40 --- /dev/null +++ b/src/cloud/ec2/lib/views/describe_regions.erb @@ -0,0 +1,10 @@ + + + + + + opennebula + <%= @base_url %> + + + \ No newline at end of file diff --git a/src/cloud/ec2/lib/views/reboot_instances.erb b/src/cloud/ec2/lib/views/reboot_instances.erb new file mode 100644 index 0000000000..9e54580536 --- /dev/null +++ b/src/cloud/ec2/lib/views/reboot_instances.erb @@ -0,0 +1,5 @@ + + + + true + \ No newline at end of file diff --git a/src/cloud/ec2/lib/views/run_instances.erb b/src/cloud/ec2/lib/views/run_instances.erb index b20fb733e6..5312eec878 100644 --- a/src/cloud/ec2/lib/views/run_instances.erb +++ b/src/cloud/ec2/lib/views/run_instances.erb @@ -1,32 +1,31 @@ - - r-47a5402e - <%= erb_user_name %> - - - default - - - - - i-<%= erb_vm_info[:vm_id] %> - <%= erb_vm_info[:ec2_img_id] %> - - 0 - pending - - <%= erb_vm_info[:vm]["TEMPLATE/NIC/IP"]%> - <%= erb_vm_info[:vm]["TEMPLATE/NIC/IP"]%> - default - 0 - <%= erb_vm_info[:instance_type] %> - <%= render_launch_time(erb_vm_info[:vm]) %> - - default - - - true - - - - + + r-47a5402e + <%= erb_user_name %> + + + default + + + + + <%= erb_instance_id %> + <%= erb_vm_info[:ec2_img_id] %> + + <%= render_state(vm) %> + + <%= vm["TEMPLATE/NIC/IP"]%> + <%= vm["TEMPLATE/NIC/IP"]%> + default + 0 + <%= erb_vm_info[:instance_type] %> + <%= render_launch_time(vm) %> + + default + + + true + + + + diff --git a/src/cloud/ec2/lib/views/start_instances.erb b/src/cloud/ec2/lib/views/start_instances.erb new file mode 100644 index 0000000000..e5ffaf6cae --- /dev/null +++ b/src/cloud/ec2/lib/views/start_instances.erb @@ -0,0 +1,13 @@ + + + + <%= erb_instance_id %> + + <%= erb_current_state %> + + + <%= erb_previous_state %> + + + + \ No newline at end of file diff --git a/src/cloud/ec2/lib/views/stop_instances.erb b/src/cloud/ec2/lib/views/stop_instances.erb new file mode 100644 index 0000000000..1c4cc1ac84 --- /dev/null +++ b/src/cloud/ec2/lib/views/stop_instances.erb @@ -0,0 +1,13 @@ + + + + <%= erb_instance_id %> + + <%= erb_current_state %> + + + <%= erb_previous_state %> + + + + \ No newline at end of file diff --git a/src/cloud/ec2/lib/views/terminate_instances.erb b/src/cloud/ec2/lib/views/terminate_instances.erb index 0accdbf283..ba8b555d25 100644 --- a/src/cloud/ec2/lib/views/terminate_instances.erb +++ b/src/cloud/ec2/lib/views/terminate_instances.erb @@ -1,14 +1,13 @@ - - - - i-<%= vm.id %> - - 32 - shutting-down - - - <%= render_state(vm) %> + + + + <%= erb_instance_id %> + + <%= erb_current_state %> + + + <%= erb_previous_state %> - - + + From a57126959db242eb715a84b240dce69861fb25df Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Tue, 14 Aug 2012 15:16:25 +0200 Subject: [PATCH 07/13] feature #378: Fix error xml --- src/cloud/ec2/bin/econe-describe-volumes | 2 +- src/cloud/ec2/lib/EC2QueryServer.rb | 22 ++++++++++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/cloud/ec2/bin/econe-describe-volumes b/src/cloud/ec2/bin/econe-describe-volumes index 4b19b3f326..049c95ebd1 100755 --- a/src/cloud/ec2/bin/econe-describe-volumes +++ b/src/cloud/ec2/bin/econe-describe-volumes @@ -126,7 +126,7 @@ end if volumes volumes.each { |vol| instances = "" - if vol['attachmentSet']['item'] + if vol['attachmentSet'] && vol['attachmentSet']['item'] instances = vol['attachmentSet']['item'].collect{ |item| item['instanceId'] }.join(',') diff --git a/src/cloud/ec2/lib/EC2QueryServer.rb b/src/cloud/ec2/lib/EC2QueryServer.rb index 2cd48f7fd3..7367c3278b 100644 --- a/src/cloud/ec2/lib/EC2QueryServer.rb +++ b/src/cloud/ec2/lib/EC2QueryServer.rb @@ -27,15 +27,23 @@ require 'elastic_ip' require 'instance' module OpenNebula + EC2_ERROR = %q{ + + + + + <%= (@ec2_code||'UnsupportedOperation') %> + <%= @message %> + + + + } + class Error attr_accessor :ec2_code def to_ec2 - xml = "" - xml << (@ec2_code||"Unsupported") - xml << "" - xml << @message - xml << "" + ERB.new(EC2_ERROR).result(binding) end end end @@ -135,7 +143,9 @@ class EC2QueryServer < CloudServer def describe_images(params) user_flag = OpenNebula::Pool::INFO_ALL impool = ImageEC2Pool.new(@client, user_flag) - impool.info + + rc = impool.info + return rc if OpenNebula::is_error?(rc) erb_version = params['Version'] From c46f87101b07746aeaaa2fb6efda4e3b183f65cf Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Tue, 14 Aug 2012 15:17:02 +0200 Subject: [PATCH 08/13] feature #378: Return UnsupportedOperation when the action is not supported --- src/cloud/ec2/lib/econe-server.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/cloud/ec2/lib/econe-server.rb b/src/cloud/ec2/lib/econe-server.rb index af82e75f31..925d643d49 100644 --- a/src/cloud/ec2/lib/econe-server.rb +++ b/src/cloud/ec2/lib/econe-server.rb @@ -208,11 +208,15 @@ def do_http_request(params) result,rc = @econe_server.detach_volume(params) when 'DeleteVolume' result,rc = @econe_server.delete_volume(params) + else + result = OpenNebula::Error.new( + "#{params['Action']} feature is not supported", + OpenNebula::Error::ENO_EXISTS) end if OpenNebula::is_error?(result) logger.error(result.message) - error CloudServer::HTTP_ERROR_CODE[result.errno], result.to_ec2 + error CloudServer::HTTP_ERROR_CODE[result.errno], result.to_ec2.gsub(/\n\s*/,'') end headers['Content-Type'] = 'application/xml' @@ -221,5 +225,7 @@ def do_http_request(params) status rc end - result + logger.error { params['Action'] } + + result.gsub(/\n\s*/,'') end From 0e5aac62d567aa63e501865e97a480f2d5160ddd Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Tue, 14 Aug 2012 15:17:38 +0200 Subject: [PATCH 09/13] feature #378: Minor fixes in attach and detach --- src/cloud/ec2/lib/ebs.rb | 43 +++++++++++++------- src/cloud/ec2/lib/instance.rb | 4 +- src/cloud/ec2/lib/views/attach_volume.erb | 2 +- src/cloud/ec2/lib/views/describe_volumes.erb | 2 + 4 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/cloud/ec2/lib/ebs.rb b/src/cloud/ec2/lib/ebs.rb index a86ee5a65f..875f245770 100644 --- a/src/cloud/ec2/lib/ebs.rb +++ b/src/cloud/ec2/lib/ebs.rb @@ -10,20 +10,33 @@ module EBS # @option params [String] Device The TARGET (unsupported) # @option params [String] Force The TARGET (unsupported) def detach_volume(params) - image_id = params['VolumeId'] + target = params['Device'] + image_id = params['VolumeId'] + vm_id_ec2 = params['InstanceId'] + image_id = image_id.split('-')[1] if image_id[0]==?v - vm_id = params['InstanceId'] - vm_id = vm_id.split('-')[1] if vm_id[0]==?i + image = Image.new(Image.build_xml(image_id), @client) + rc = image.info + return rc if OpenNebula::is_error?(rc) - target = params['Device'] + if !vm_id_ec2 + vm_id_ec2 = image["TEMPLATE/EBS/INSTANCE_ID"] + end + vm_id = vm_id_ec2.split('-')[1] if vm_id_ec2[0]==?i + + if vm_id.nil? + rc = OpenNebula::Error.new("The volume #{params['VolumeId']} is\ + not attached to any instance") + logger.error {rc.message} + return rc + end # Detach vm = VirtualMachine.new(VirtualMachine.build_xml(vm_id), @client) rc = vm.info - return rc if OpenNebula::is_error?(rc) disk_id = vm["TEMPLATE/DISK[IMAGE_ID=#{image_id.to_i}]/DISK_ID"] @@ -40,15 +53,10 @@ module EBS return rc if OpenNebula::is_error?(rc) + attach_time = image["TEMPLATE/EBS[INSTANCE_ID=\"#{vm_id_ec2}\"]/ATTACH_TIME"] # Update IMAGE metadata - - image = Image.new(Image.build_xml(image_id), @client) - rc = image.info - - return rc if OpenNebula::is_error?(rc) - - image.delete_element("TEMPLATE/EBS[INSTANCE_ID=\"#{params['InstanceId']}\"]") + image.delete_element("TEMPLATE/EBS[INSTANCE_ID=\"#{vm_id_ec2}\"]") rc = image.update if OpenNebula::is_error?(rc) logger.error {rc.message} @@ -94,7 +102,7 @@ module EBS return rc if OpenNebula::is_error?(rc) template = "DISK = [ IMAGE_ID = #{image_id}, TARGET = #{target} ]" - vm.attachdisk(template) + rc = vm.attachdisk(template) return rc if OpenNebula::is_error?(rc) @@ -106,9 +114,12 @@ module EBS return rc if OpenNebula::is_error?(rc) + attach_time = Time.now.to_i + xml_hash = {'EBS' => { 'INSTANCE_ID' => params['InstanceId'], - "DEVICE" => params['Device']} + "DEVICE" => params['Device'], + "ATTACH_TIME" => attach_time} } image.add_element('TEMPLATE', xml_hash) @@ -192,7 +203,9 @@ module EBS def describe_volumes(params) user_flag = OpenNebula::Pool::INFO_ALL impool = ImageEC2Pool.new(@client, user_flag) - impool.info + rc = impool.info + + return rc if OpenNebula::is_error?(rc) erb_version = params['Version'] diff --git a/src/cloud/ec2/lib/instance.rb b/src/cloud/ec2/lib/instance.rb index b263982003..8447ee2867 100644 --- a/src/cloud/ec2/lib/instance.rb +++ b/src/cloud/ec2/lib/instance.rb @@ -79,7 +79,9 @@ module Instance def describe_instances(params) user_flag = OpenNebula::Pool::INFO_ALL vmpool = VirtualMachinePool.new(@client, user_flag) - vmpool.info + + rc = vmpool.info + return rc if OpenNebula::is_error?(rc) erb_version = params['Version'] erb_user_name = params['AWSAccessKeyId'] diff --git a/src/cloud/ec2/lib/views/attach_volume.erb b/src/cloud/ec2/lib/views/attach_volume.erb index 869202f933..5a09bfb3ef 100644 --- a/src/cloud/ec2/lib/views/attach_volume.erb +++ b/src/cloud/ec2/lib/views/attach_volume.erb @@ -5,5 +5,5 @@ i-<%= sprintf('%08i', vm_id) %> /dev/<%= vm["TEMPLATE/DISK[IMAGE_ID=#{image_id.to_i}]/TARGET"] %> attaching - + <%= Time.at(attach_time).xmlschema %> diff --git a/src/cloud/ec2/lib/views/describe_volumes.erb b/src/cloud/ec2/lib/views/describe_volumes.erb index aa92d12974..de855dd404 100644 --- a/src/cloud/ec2/lib/views/describe_volumes.erb +++ b/src/cloud/ec2/lib/views/describe_volumes.erb @@ -18,6 +18,8 @@ <%= ebs['INSTANCE_ID'] %> <%= ebs['DEVICE'] %> attached + <%= Time.at(ebs['ATTACH_TIME'].to_i).xmlschema %> + false <% end %> From 6e862acbe6910c48378f2a8c40a7da50867631ca Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Fri, 17 Aug 2012 13:51:38 +0200 Subject: [PATCH 10/13] feature #378: Small fixes in econe CLI --- src/cloud/ec2/bin/econe-describe-addresses | 13 ++++----- src/cloud/ec2/bin/econe-describe-images | 31 +++++++++++----------- src/cloud/ec2/bin/econe-describe-instances | 16 ++++++----- src/cloud/ec2/bin/econe-describe-volumes | 25 ++++++++--------- src/cloud/ec2/bin/econe-detach-volume | 5 ---- src/cloud/ec2/lib/ebs.rb | 6 ++--- src/cloud/ec2/lib/views/attach_volume.erb | 2 +- src/cloud/ec2/lib/views/detach_volume.erb | 2 +- 8 files changed, 51 insertions(+), 49 deletions(-) diff --git a/src/cloud/ec2/bin/econe-describe-addresses b/src/cloud/ec2/bin/econe-describe-addresses index f799407ed7..fae2473b30 100755 --- a/src/cloud/ec2/bin/econe-describe-addresses +++ b/src/cloud/ec2/bin/econe-describe-addresses @@ -114,7 +114,10 @@ if CloudClient::is_error?(rc) exit -1 end -addresses = rc['addressesSet']['item'] +addresses = [] +if rc['addressesSet'] + addresses = rc['addressesSet']['item'] +end fmt = "%-12s %s" @@ -123,11 +126,9 @@ if headers puts "------------------------------------------------------------------------------" end -if addresses - addresses.each { |addr| - puts fmt % [addr['publicIp'],addr['instanceId']] - } -end +addresses.each { |addr| + puts fmt % [addr['publicIp'],addr['instanceId']] +} exit 0 diff --git a/src/cloud/ec2/bin/econe-describe-images b/src/cloud/ec2/bin/econe-describe-images index 4dfbff2789..72bb373467 100755 --- a/src/cloud/ec2/bin/econe-describe-images +++ b/src/cloud/ec2/bin/econe-describe-images @@ -51,7 +51,7 @@ Options: --headers, -H Display column headers - + EOT require 'econe/EC2QueryClient' @@ -97,7 +97,7 @@ begin end rescue Exception => e exit -1 -end +end auth = "#{access}:#{secret}" if secret && access @@ -115,25 +115,26 @@ if CloudClient::is_error?(rc) exit -1 end -images = rc['imagesSet']['item'] +images = [] +if rc['imagesSet'] + images = rc['imagesSet']['item'] +end -fmt = "%-12s %-13s %-14s %-12s %s" +fmt = "%-12s %-13s %-14s %-12s %s" if headers puts fmt % ["Owner", "ImageId", "Status", "Visibility", "Location"] - puts "------------------------------------------------------------------------------" + puts "------------------------------------------------------------------------------" end -if images - images.each { |img| - if img['isPublic'] == 'true' - visibility = "public" - elsif img['isPublic'] == 'false' - visibility = "private" - end - puts fmt % [img['imageOwnerId'],img['imageId'], img['imageState'], visibility,img['imageLocation']] - } -end +images.each { |img| + if img['isPublic'] == 'true' + visibility = "public" + elsif img['isPublic'] == 'false' + visibility = "private" + end + puts fmt % [img['imageOwnerId'],img['imageId'], img['imageState'], visibility,img['imageLocation']] +} exit 0 diff --git a/src/cloud/ec2/bin/econe-describe-instances b/src/cloud/ec2/bin/econe-describe-instances index eeae5dfce2..15bafd4bd0 100755 --- a/src/cloud/ec2/bin/econe-describe-instances +++ b/src/cloud/ec2/bin/econe-describe-instances @@ -113,7 +113,11 @@ if CloudClient::is_error?(rc) exit -1 end -instances = rc['reservationSet']['item'][0]['instancesSet']['item'] +instances = [] +if rc['reservationSet']['item'][0]['instancesSet'] + instances = rc['reservationSet']['item'][0]['instancesSet']['item'] +end + owner = rc['reservationSet']['item'][0]['ownerId'] fmt = "%-10s %-11s %-13s %-11s %-15s %-10s" @@ -123,9 +127,9 @@ if headers puts "-----------------------------------------------------------------------------------" end -if instances - instances.each { |img| - puts fmt % [owner, img['instanceId'],img['imageId'],img['instanceState']['name'],img['dnsName'],img['instanceType']] - } -end + +instances.each { |img| + puts fmt % [owner, img['instanceId'],img['imageId'],img['instanceState']['name'],img['dnsName'],img['instanceType']] +} + exit 0 diff --git a/src/cloud/ec2/bin/econe-describe-volumes b/src/cloud/ec2/bin/econe-describe-volumes index 049c95ebd1..14c32f291b 100755 --- a/src/cloud/ec2/bin/econe-describe-volumes +++ b/src/cloud/ec2/bin/econe-describe-volumes @@ -114,7 +114,10 @@ if CloudClient::is_error?(rc) exit -1 end -volumes = rc['volumeSet']['item'] +volumes = [] +if rc['volumeSet'] + volumes = rc['volumeSet']['item'] +end fmt = "%-15s %-10s %s" @@ -123,18 +126,16 @@ if headers puts "------------------------------------------------------------------------------" end -if volumes - volumes.each { |vol| - instances = "" - if vol['attachmentSet'] && vol['attachmentSet']['item'] - instances = vol['attachmentSet']['item'].collect{ |item| - item['instanceId'] - }.join(',') - end +volumes.each { |vol| + instances = "" + if vol['attachmentSet'] && vol['attachmentSet']['item'] + instances = vol['attachmentSet']['item'].collect{ |item| + item['instanceId'] + }.join(',') + end - puts fmt % [vol['volumeId'], vol['size'], instances] - } -end + puts fmt % [vol['volumeId'], vol['size'], instances] +} exit 0 diff --git a/src/cloud/ec2/bin/econe-detach-volume b/src/cloud/ec2/bin/econe-detach-volume index 5238b3bb3d..666092d2a9 100755 --- a/src/cloud/ec2/bin/econe-detach-volume +++ b/src/cloud/ec2/bin/econe-detach-volume @@ -123,11 +123,6 @@ if !volume_id exit -1 end -if !instance - puts "#{cmd_name}: missing --instance option" - exit -1 -end - auth = "#{access}:#{secret}" if secret && access begin diff --git a/src/cloud/ec2/lib/ebs.rb b/src/cloud/ec2/lib/ebs.rb index 875f245770..707edfef33 100644 --- a/src/cloud/ec2/lib/ebs.rb +++ b/src/cloud/ec2/lib/ebs.rb @@ -24,15 +24,15 @@ module EBS vm_id_ec2 = image["TEMPLATE/EBS/INSTANCE_ID"] end - vm_id = vm_id_ec2.split('-')[1] if vm_id_ec2[0]==?i - - if vm_id.nil? + if vm_id_ec2.nil? rc = OpenNebula::Error.new("The volume #{params['VolumeId']} is\ not attached to any instance") logger.error {rc.message} return rc end + vm_id = vm_id_ec2.split('-')[1] if vm_id_ec2[0]==?i + # Detach vm = VirtualMachine.new(VirtualMachine.build_xml(vm_id), @client) diff --git a/src/cloud/ec2/lib/views/attach_volume.erb b/src/cloud/ec2/lib/views/attach_volume.erb index 5a09bfb3ef..6c08d7884c 100644 --- a/src/cloud/ec2/lib/views/attach_volume.erb +++ b/src/cloud/ec2/lib/views/attach_volume.erb @@ -2,7 +2,7 @@ vol-<%= sprintf('%08i', image_id) %> - i-<%= sprintf('%08i', vm_id) %> + <%= render_instance_id(vm) %> /dev/<%= vm["TEMPLATE/DISK[IMAGE_ID=#{image_id.to_i}]/TARGET"] %> attaching <%= Time.at(attach_time).xmlschema %> diff --git a/src/cloud/ec2/lib/views/detach_volume.erb b/src/cloud/ec2/lib/views/detach_volume.erb index 0d8306a486..ae1df87487 100644 --- a/src/cloud/ec2/lib/views/detach_volume.erb +++ b/src/cloud/ec2/lib/views/detach_volume.erb @@ -2,7 +2,7 @@ vol-<%= sprintf('%08i', image_id) %> - i-<%= sprintf('%08i', vm_id) %> + <%= render_instance_id(vm) %> <%= target %> detaching From b9c486238ba8f4bb06621cbe3e342dfb5c07d334 Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Thu, 30 Aug 2012 19:07:01 +0200 Subject: [PATCH 11/13] feature #378: Improve EBS functionality * Add LICENSE headers * Check if a volume is already attached * Add EBS_VOLUME parameter when an image is created using CreateVolume --- src/cloud/ec2/lib/ImageEC2.rb | 3 ++ src/cloud/ec2/lib/ebs.rb | 37 +++++++++++++++----- src/cloud/ec2/lib/elastic_ip.rb | 16 +++++++++ src/cloud/ec2/lib/instance.rb | 16 +++++++++ src/cloud/ec2/lib/views/describe_images.erb | 2 +- src/cloud/ec2/lib/views/describe_volumes.erb | 2 +- 6 files changed, 66 insertions(+), 10 deletions(-) diff --git a/src/cloud/ec2/lib/ImageEC2.rb b/src/cloud/ec2/lib/ImageEC2.rb index 214089a970..585fedd81f 100644 --- a/src/cloud/ec2/lib/ImageEC2.rb +++ b/src/cloud/ec2/lib/ImageEC2.rb @@ -55,6 +55,9 @@ class ImageEC2 < Image <% if @image_info[:persistent] != nil %> PERSISTENT = "YES" <% end %> + <% if @image_info[:ebs] != "nil" %> + EBS_VOLUME = "YES" + <% end %> <% if @image_file != nil %> PATH = "<%= @image_file %>" <% end %> diff --git a/src/cloud/ec2/lib/ebs.rb b/src/cloud/ec2/lib/ebs.rb index 707edfef33..bf47296bde 100644 --- a/src/cloud/ec2/lib/ebs.rb +++ b/src/cloud/ec2/lib/ebs.rb @@ -1,3 +1,19 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org) # +# # +# Licensed 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 EBS # Default FSTYPE when creating new volumes DEFAULT_FSTYPE = "ext3" @@ -93,9 +109,19 @@ module EBS target = m[1] end + # Check if the volume is already attached to another instance + image = Image.new(Image.build_xml(image_id), @client) + rc = image.info + + return rc if OpenNebula::is_error?(rc) + + if image['TEMPLATE/EBS/INSTANCE_ID'] + return OpenNebula::Error.new("Volume #{params['VolumeId']} \ + already attached to another instance \ + (#{image['TEMPLATE/EBS/INSTANCE_ID']})") + end # Attach - vm = VirtualMachine.new(VirtualMachine.build_xml(vm_id), @client) rc = vm.info @@ -108,12 +134,6 @@ module EBS # Update IMAGE metadata - - image = Image.new(Image.build_xml(image_id), @client) - rc = image.info - - return rc if OpenNebula::is_error?(rc) - attach_time = Time.now.to_i xml_hash = {'EBS' => { @@ -156,7 +176,8 @@ module EBS :type => "DATABLOCK", :size => size, :fstype => @config[:ebs_fstype]||DEFAULT_FSTYPE, - :persistent => "YES" + :persistent => "YES", + :ebs => "YES" } image = ImageEC2.new(Image.build_xml, @client, nil, opts) diff --git a/src/cloud/ec2/lib/elastic_ip.rb b/src/cloud/ec2/lib/elastic_ip.rb index a495a8c9a6..7ee2afb7a1 100644 --- a/src/cloud/ec2/lib/elastic_ip.rb +++ b/src/cloud/ec2/lib/elastic_ip.rb @@ -1,3 +1,19 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org) # +# # +# Licensed 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 ElasticIP def allocate_address(params) # Get public IP diff --git a/src/cloud/ec2/lib/instance.rb b/src/cloud/ec2/lib/instance.rb index 8447ee2867..91054d3f01 100644 --- a/src/cloud/ec2/lib/instance.rb +++ b/src/cloud/ec2/lib/instance.rb @@ -1,3 +1,19 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org) # +# # +# Licensed 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 Instance ########################################################################### # Class Constants. Defined the EC2 and OpenNebula State mapping diff --git a/src/cloud/ec2/lib/views/describe_images.erb b/src/cloud/ec2/lib/views/describe_images.erb index bb590af948..bae66fafef 100644 --- a/src/cloud/ec2/lib/views/describe_images.erb +++ b/src/cloud/ec2/lib/views/describe_images.erb @@ -2,7 +2,7 @@ <% impool.each do |im| %> - <% if state_image = im.render_state %> + <% if (state_image = im.render_state) && (im['TEMPLATE/EBS'] != 'YES') %> ami-<%= sprintf('%08i', im.id) %> <%= im['SOURCE'].split('/').last %> diff --git a/src/cloud/ec2/lib/views/describe_volumes.erb b/src/cloud/ec2/lib/views/describe_volumes.erb index de855dd404..8ad1dad271 100644 --- a/src/cloud/ec2/lib/views/describe_volumes.erb +++ b/src/cloud/ec2/lib/views/describe_volumes.erb @@ -3,7 +3,7 @@ <% impool.each do |im| %> - <% if state_image = im.render_state %> + <% if (state_image = im.render_state) && (im['TEMPLATE/EBS'] == 'YES') %> vol-<%= sprintf('%08i', im.id) %> <%= im.render_size %> From e49d79b9c260d040bfb80fb6850b902791a5bf8c Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Thu, 30 Aug 2012 20:10:19 +0200 Subject: [PATCH 12/13] feature #378: Use EBS_VOLUME instead of EBS when filtering images --- src/cloud/ec2/lib/ebs.rb | 6 +++--- src/cloud/ec2/lib/views/describe_images.erb | 2 +- src/cloud/ec2/lib/views/describe_volumes.erb | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cloud/ec2/lib/ebs.rb b/src/cloud/ec2/lib/ebs.rb index bf47296bde..058619ba81 100644 --- a/src/cloud/ec2/lib/ebs.rb +++ b/src/cloud/ec2/lib/ebs.rb @@ -116,9 +116,9 @@ module EBS return rc if OpenNebula::is_error?(rc) if image['TEMPLATE/EBS/INSTANCE_ID'] - return OpenNebula::Error.new("Volume #{params['VolumeId']} \ - already attached to another instance \ - (#{image['TEMPLATE/EBS/INSTANCE_ID']})") + return OpenNebula::Error.new("Volume #{params['VolumeId']} " << + "already attached to another instance " << + "(#{image['TEMPLATE/EBS/INSTANCE_ID']})") end # Attach diff --git a/src/cloud/ec2/lib/views/describe_images.erb b/src/cloud/ec2/lib/views/describe_images.erb index bae66fafef..6238709ab0 100644 --- a/src/cloud/ec2/lib/views/describe_images.erb +++ b/src/cloud/ec2/lib/views/describe_images.erb @@ -2,7 +2,7 @@ <% impool.each do |im| %> - <% if (state_image = im.render_state) && (im['TEMPLATE/EBS'] != 'YES') %> + <% if (state_image = im.render_state) && (im['TEMPLATE/EBS_VOLUME'] != 'YES') %> ami-<%= sprintf('%08i', im.id) %> <%= im['SOURCE'].split('/').last %> diff --git a/src/cloud/ec2/lib/views/describe_volumes.erb b/src/cloud/ec2/lib/views/describe_volumes.erb index 8ad1dad271..be721a5575 100644 --- a/src/cloud/ec2/lib/views/describe_volumes.erb +++ b/src/cloud/ec2/lib/views/describe_volumes.erb @@ -3,7 +3,7 @@ <% impool.each do |im| %> - <% if (state_image = im.render_state) && (im['TEMPLATE/EBS'] == 'YES') %> + <% if (state_image = im.render_state) && (im['TEMPLATE/EBS_VOLUME'] == 'YES') %> vol-<%= sprintf('%08i', im.id) %> <%= im.render_size %> From 50d8e31c362e3b6c21824cddfe41910524baf824 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Fri, 31 Aug 2012 22:42:02 +0200 Subject: [PATCH 13/13] feature #378: Some minor changes --- src/cloud/ec2/lib/ebs.rb | 61 ++++++++++++++++++----------------- src/cloud/ec2/lib/instance.rb | 2 +- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/cloud/ec2/lib/ebs.rb b/src/cloud/ec2/lib/ebs.rb index 058619ba81..2cb97fc12b 100644 --- a/src/cloud/ec2/lib/ebs.rb +++ b/src/cloud/ec2/lib/ebs.rb @@ -30,10 +30,10 @@ module EBS image_id = params['VolumeId'] vm_id_ec2 = params['InstanceId'] - image_id = image_id.split('-')[1] if image_id[0]==?v + image_id = image_id.split('-')[1] if image_id[0] == "v" image = Image.new(Image.build_xml(image_id), @client) - rc = image.info + rc = image.info return rc if OpenNebula::is_error?(rc) if !vm_id_ec2 @@ -47,12 +47,13 @@ module EBS return rc end - vm_id = vm_id_ec2.split('-')[1] if vm_id_ec2[0]==?i + vm_id = vm_id_ec2.split('-')[1] if vm_id_ec2[0] == "i" # Detach vm = VirtualMachine.new(VirtualMachine.build_xml(vm_id), @client) rc = vm.info + return rc if OpenNebula::is_error?(rc) disk_id = vm["TEMPLATE/DISK[IMAGE_ID=#{image_id.to_i}]/DISK_ID"] @@ -69,22 +70,20 @@ module EBS return rc if OpenNebula::is_error?(rc) - attach_time = image["TEMPLATE/EBS[INSTANCE_ID=\"#{vm_id_ec2}\"]/ATTACH_TIME"] - # Update IMAGE metadata - image.delete_element("TEMPLATE/EBS[INSTANCE_ID=\"#{vm_id_ec2}\"]") + image.delete_element("TEMPLATE/EBS") + rc = image.update + if OpenNebula::is_error?(rc) logger.error {rc.message} return rc end - # Response - erb_version = params['Version'] + response = ERB.new(File.read(@config[:views]+"/detach_volume.erb")) - response = ERB.new(File.read(@config[:views]+"/detach_volume.erb")) return response.result(binding), 200 end @@ -99,19 +98,20 @@ module EBS # instance (e.g., /dev/sdh, or xvdh) def attach_volume(params) image_id = params['VolumeId'] - image_id = image_id.split('-')[1] if image_id[0]==?v + image_id = image_id.split('-')[1] if image_id[0] == "v" vm_id = params['InstanceId'] - vm_id = vm_id.split('-')[1] if vm_id[0]==?i + vm_id = vm_id.split('-')[1] if vm_id[0] == "i" target = params['Device'] + if m = target.match(/^\/dev\/(\w+)$/) target = m[1] end # Check if the volume is already attached to another instance image = Image.new(Image.build_xml(image_id), @client) - rc = image.info + rc = image.info return rc if OpenNebula::is_error?(rc) @@ -128,35 +128,35 @@ module EBS return rc if OpenNebula::is_error?(rc) template = "DISK = [ IMAGE_ID = #{image_id}, TARGET = #{target} ]" - rc = vm.attachdisk(template) + rc = vm.attachdisk(template) return rc if OpenNebula::is_error?(rc) - # Update IMAGE metadata attach_time = Time.now.to_i xml_hash = {'EBS' => { 'INSTANCE_ID' => params['InstanceId'], "DEVICE" => params['Device'], - "ATTACH_TIME" => attach_time} + "ATTACH_TIME" => attach_time + } } image.add_element('TEMPLATE', xml_hash) + rc = image.update + if OpenNebula::is_error?(rc) logger.error rc.message return rc end - # Response - - erb_version = params['Version'] - vm.info - response = ERB.new(File.read(@config[:views]+"/attach_volume.erb")) + erb_version = params['Version'] + response = ERB.new(File.read(@config[:views]+"/attach_volume.erb")) + return response.result(binding), 200 end @@ -180,23 +180,25 @@ module EBS :ebs => "YES" } - image = ImageEC2.new(Image.build_xml, @client, nil, opts) - + image = ImageEC2.new(Image.build_xml, @client, nil, opts) template = image.to_one_template + if OpenNebula.is_error?(template) return template end rc = image.allocate(template, @config[:datastore_id]||1) + if OpenNebula.is_error?(rc) return rc end - erb_version = params['Version'] - + # Response image.info - response = ERB.new(File.read(@config[:views]+"/create_volume.erb")) + erb_version = params['Version'] + response = ERB.new(File.read(@config[:views]+"/create_volume.erb")) + return response.result(binding), 200 end @@ -209,13 +211,13 @@ module EBS image_id = image_id.split('-')[1] if image_id[0]==?v image = ImageEC2.new(Image.build_xml(image_id), @client) - rc = image.delete + rc = image.delete return rc if OpenNebula::is_error?(rc) erb_version = params['Version'] + response = ERB.new(File.read(@config[:views]+"/delete_volume.erb")) - response = ERB.new(File.read(@config[:views]+"/delete_volume.erb")) return response.result(binding), 200 end @@ -223,14 +225,15 @@ module EBS # Describes your Amazon EBS volumes def describe_volumes(params) user_flag = OpenNebula::Pool::INFO_ALL - impool = ImageEC2Pool.new(@client, user_flag) + impool = ImageEC2Pool.new(@client, user_flag) + rc = impool.info return rc if OpenNebula::is_error?(rc) erb_version = params['Version'] + response = ERB.new(File.read(@config[:views]+"/describe_volumes.erb")) - response = ERB.new(File.read(@config[:views]+"/describe_volumes.erb")) return response.result(binding), 200 end diff --git a/src/cloud/ec2/lib/instance.rb b/src/cloud/ec2/lib/instance.rb index 91054d3f01..951c080dd4 100644 --- a/src/cloud/ec2/lib/instance.rb +++ b/src/cloud/ec2/lib/instance.rb @@ -149,7 +149,7 @@ module Instance vmid=params['InstanceId.1'] vmid=params['InstanceId.01'] if !vmid - tmp, vmid=vmid.split('-') if vmid[0]==?i + tmp, vmid=vmid.split('-') if vmid[0] == "i" vm = VirtualMachine.new(VirtualMachine.build_xml(vmid),@client)