diff --git a/install.sh b/install.sh index 70dfc3f939..bc5d6a6505 100755 --- a/install.sh +++ b/install.sh @@ -1320,6 +1320,7 @@ 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/tags.rb \ src/cloud/ec2/lib/instance.rb \ src/cloud/ec2/lib/keypair.rb \ src/cloud/ec2/lib/net_ssh_replacement.rb \ @@ -1332,7 +1333,14 @@ 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_tags.erb \ + src/cloud/ec2/lib/views/delete_tags.erb \ + src/cloud/ec2/lib/views/describe_tags.erb \ src/cloud/ec2/lib/views/create_volume.erb \ + src/cloud/ec2/lib/views/create_snapshot.erb \ + src/cloud/ec2/lib/views/delete_snapshot.erb \ + src/cloud/ec2/lib/views/describe_snapshots.erb \ + src/cloud/ec2/lib/views/create_image.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 \ diff --git a/src/cloud/ec2/etc/econe.conf b/src/cloud/ec2/etc/econe.conf index ee43a6a706..c8bb64e440 100644 --- a/src/cloud/ec2/etc/econe.conf +++ b/src/cloud/ec2/etc/econe.conf @@ -64,6 +64,20 @@ # default 1 #:datastore_id: +# Include terminated instances in the describe_instances xml +:describe_with_terminated_instances: = true +# Terminated VMs will be included in the list +# till the termination date + TERMINATED_INSTANCES_EXPIRATION_TIME is reached +:terminated_instances_expiration_time: 900 + + +############################################################# +# [DEPRECATED] File based templates +############################################################# + +# Use former file based templates for instance types instead +# of OpenNebula templates +:use_file_templates: false # VM types allowed and its template file (inside templates directory) :instance_types: diff --git a/src/cloud/ec2/lib/EC2QueryServer.rb b/src/cloud/ec2/lib/EC2QueryServer.rb index f40d406825..bd8b4c460e 100644 --- a/src/cloud/ec2/lib/EC2QueryServer.rb +++ b/src/cloud/ec2/lib/EC2QueryServer.rb @@ -28,6 +28,7 @@ require 'ebs' require 'elastic_ip' require 'instance' require 'keypair' +require 'tags' ################################################################################ # Extends the OpenNebula::Error class to include an EC2 render of error @@ -126,20 +127,30 @@ class EC2QueryServer < CloudServer return response.result(binding), 200 end + # TODO Kernel, Ramdisk, Arch, BlockDeviceMapping def register_image(params) # Get the Image ID - tmp, img=params['ImageLocation'].split('-') + image_id = params['ImageLocation'] + image = ImageEC2.new(Image.build_xml(image_id.to_i), @client) - image = Image.new(Image.build_xml(img.to_i), @client) - - # Enable the new Image rc = image.info if OpenNebula.is_error?(rc) - rc.ec2_code = "InvalidAMIID.NotFound" return rc end - image.enable + if image["EBS_VOLUME"] == "YES" + return OpenNebula::Error.new("The image you are trying to register"\ + " is already a volume") + elsif image["EBS_SNAPSHOT"] == "YES" + return OpenNebula::Error.new("The image you are trying to register"\ + " is already an snapshot") + end + + image.add_element('TEMPLATE', {"EC2_AMI" => "YES"}) + rc = image.update + if OpenNebula.is_error?(rc) + return rc + end erb_version = params['Version'] @@ -148,11 +159,34 @@ class EC2QueryServer < CloudServer end def describe_images(params) - user_flag = OpenNebula::Pool::INFO_ALL - impool = ImageEC2Pool.new(@client, user_flag) + impool = [] + params.each { |key, value| + if key =~ /ImageId\./ + if value =~ /ami\-(.+)/ + image = ImageEC2.new(Image.build_xml($1), @client) + rc = image.info + if OpenNebula.is_error?(rc) || !image.ec2_ami? + rc ||= OpenNebula::Error.new() + rc.ec2_code = "InvalidAMIID.NotFound" + return rc + else + impool << image + end + else + rc = OpenNebula::Error.new("InvalidAMIID.Malformed #{value}") + rc.ec2_code = "InvalidAMIID.Malformed" + return rc + end + end + } - rc = impool.info - return rc if OpenNebula::is_error?(rc) + if impool.empty? + user_flag = OpenNebula::Pool::INFO_ALL + impool = ImageEC2Pool.new(@client, user_flag) + + rc = impool.info + return rc if OpenNebula::is_error?(rc) + end erb_version = params['Version'] @@ -160,6 +194,43 @@ class EC2QueryServer < CloudServer return response.result(binding), 200 end + # TODO: NoReboot = false, cleanly shut down the instance before image + # creation and then reboots the instance. + # TODO: If you customized your instance with instance store volumes + # or EBS volumes in addition to the root device volume, the + # new AMI contains block device mapping information for those volumes + def create_image(params) + instance_id = params['InstanceId'] + instance_id = instance_id.split('-')[1] + + vm = VirtualMachine.new( + VirtualMachine.build_xml(instance_id), + @client) + + rc = vm.info + if OpenNebula::is_error?(rc) + rc.ec2_code = "InvalidInstanceID.NotFound" + return rc + end + + image_id = vm.disk_snapshot(1, + params["Name"], + OpenNebula::Image::IMAGE_TYPES[0], + true) + + # TODO Add AMI Tags + # TODO A new persistent image should be created for each instance + + if OpenNebula::is_error?(image_id) + return image_id + end + + erb_version = params['Version'] + + response = ERB.new(File.read(@config[:views]+"/create_image.erb")) + return response.result(binding), 200 + end + ########################################################################### # Helper functions ########################################################################### diff --git a/src/cloud/ec2/lib/ImageEC2.rb b/src/cloud/ec2/lib/ImageEC2.rb index 448ad07fd1..6939b67d1e 100644 --- a/src/cloud/ec2/lib/ImageEC2.rb +++ b/src/cloud/ec2/lib/ImageEC2.rb @@ -44,7 +44,7 @@ class ImageEC2 < Image } ONE_IMAGE = %q{ - NAME = "ec2-<%= uuid %>" + NAME = "<%= ImageEC2.generate_uuid %>" TYPE = <%= @image_info[:type] %> <% if @image_info[:size] != nil %> SIZE = "<%= @image_info[:size] %>" @@ -55,7 +55,7 @@ class ImageEC2 < Image <% if @image_info[:persistent] != nil %> PERSISTENT = "YES" <% end %> - <% if @image_info[:ebs] != nil %> + <% if @image_info[:ebs_volume] != nil %> EBS_VOLUME = "YES" <% end %> <% if @image_file != nil %> @@ -74,8 +74,6 @@ class ImageEC2 < Image end def to_one_template() - uuid = UUIDTools::UUID.random_create.to_s - one = ERB.new(ONE_IMAGE) return one.result(binding) end @@ -91,4 +89,40 @@ class ImageEC2 < Image def render_create_time Time.at(self["REGTIME"].to_i).xmlschema end + + def self.generate_uuid + "ec2-" + UUIDTools::UUID.random_create.to_s + end + + def ebs_volume? + self["TEMPLATE/EBS_VOLUME"] == "YES" + end + + def ec2_ami? + self["TEMPLATE/EC2_AMI"] == "YES" + end + + def ebs_snapshot? + self["TEMPLATE/EBS_SNAPSHOT"] == "YES" + end + + def ec2_id + if self.ebs_snapshot? + "snap-" + sprintf('%08i', self.id) + elsif self.ec2_ami? + "ami-" + sprintf('%08i', self.id) + elsif self.ebs_volume? + "vol-" + sprintf('%08i', self.id) + end + end + + def resource_type + if self.ebs_snapshot? + "snapshot" + elsif self.ec2_ami? + "image" + elsif self.ebs_volume? + "volume" + end + end end diff --git a/src/cloud/ec2/lib/ebs.rb b/src/cloud/ec2/lib/ebs.rb index e5c2ed7a63..c2b7b3a26e 100644 --- a/src/cloud/ec2/lib/ebs.rb +++ b/src/cloud/ec2/lib/ebs.rb @@ -168,34 +168,65 @@ module EBS # the new volume (unsupported). # @option params [String] AvailabilityZone The Availability Zone in which # to create the new volume (unsupported) + # TODO: Availability zone def create_volume(params) - size = params['Size'].to_i # in GiBs - size *= 1024 + snapshot_id = params['SnapshotId'] + if snapshot_id + snapshot_id = snapshot_id.split('-')[1] - opts = { - :type => "DATABLOCK", - :size => size, - :fstype => @config[:ebs_fstype]||DEFAULT_FSTYPE, - :persistent => "YES", - :ebs => "YES" - } + snapshot = ImageEC2.new(Image.build_xml(snapshot_id.to_i), @client) + rc = snapshot.info + if OpenNebula::is_error?(rc) || !snapshot.ebs_snapshot? + rc ||= OpenNebula::Error.new() + rc.ec2_code = "InvalidSnapshot.NotFound" + return rc + end - image = ImageEC2.new(Image.build_xml, @client, nil, opts) - template = image.to_one_template + # Clone + volume_id = snapshot.clone(ImageEC2.generate_uuid) + if OpenNebula::is_error?(volume_id) + return volume_id + end - if OpenNebula.is_error?(template) - return template + volume = ImageEC2.new(Image.build_xml(volume_id.to_i), @client) + rc = volume.info + if OpenNebula::is_error?(rc) + return rc + end + + volume.delete_element("TEMPLATE/EBS_SNAPSHOT") + volume.add_element('TEMPLATE', { + "EBS_VOLUME" => "YES", + "EBS_FROM_SNAPSHOT_ID" => snapshot.ec2_id}) + volume.update + else + size = params['Size'].to_i # in GiBs + size *= 1024 + + opts = { + :type => "DATABLOCK", + :size => size, + :fstype => @config[:ebs_fstype]||DEFAULT_FSTYPE, + :persistent => "YES", + :ebs_volume => "YES" + } + + volume = ImageEC2.new(Image.build_xml, @client, nil, opts) + template = volume.to_one_template + + if OpenNebula.is_error?(template) + return template + end + + rc = volume.allocate(template, @config[:datastore_id]||1) + + if OpenNebula.is_error?(rc) + return rc + end + + volume.info end - rc = image.allocate(template, @config[:datastore_id]||1) - - if OpenNebula.is_error?(rc) - return rc - end - - # Response - image.info - erb_version = params['Version'] response = ERB.new(File.read(@config[:views]+"/create_volume.erb")) @@ -237,4 +268,134 @@ module EBS return response.result(binding), 200 end + # Creates a snapshot of an Amazon EBS volume + # + # @param [Hash] params + # @option params [String] VolumeId The ID of the Amazon EBS volume. + # @option params [String] Description A description for the snapshot. + def create_snapshot(params) + image_id = params['VolumeId'] + image_id = image_id.split('-')[1] + + image = ImageEC2.new(Image.build_xml(image_id.to_i), @client) + rc = image.info + if OpenNebula::is_error?(rc) || !image.ebs_volume? + rc ||= OpenNebula::Error.new() + rc.ec2_code = "InvalidVolume.NotFound" + return rc + end + + instance_id = image["TEMPLATE/EBS/INSTANCE_ID"] + + if instance_id + # Disk snapshot + instance_id = instance_id.split('-')[1] + vm = VirtualMachine.new( + VirtualMachine.build_xml(instance_id), + @client) + + rc = vm.info + if OpenNebula::is_error?(rc) + rc.ec2_code = "InvalidInstanceID.NotFound" + return rc + end + + disk_id = vm["TEMPLATE/DISK[IMAGE_ID=#{image_id}]/DISK_ID"] + if !disk_id.nil? + snapshot_id = vm.disk_snapshot(disk_id.to_i, + params["Description"]||ImageEC2.generate_uuid, + OpenNebula::Image::IMAGE_TYPES[image["TYPE"].to_i], + true) + + if OpenNebula::is_error?(snapshot_id) + return snapshot_id + end + end + end + + if snapshot_id.nil? + # Clone + snapshot_id = image.clone(params["Description"]||ImageEC2.generate_uuid) + if OpenNebula::is_error?(snapshot_id) + return snapshot_id + end + end + + snapshot = ImageEC2.new(Image.build_xml(snapshot_id.to_i), @client) + rc = snapshot.info + if OpenNebula::is_error?(rc) + return rc + end + + snapshot.delete_element("TEMPLATE/EBS_VOLUME") + snapshot.add_element('TEMPLATE', {"EBS_SNAPSHOT" => "YES"}) + snapshot.update + + erb_version = params['Version'] + + response = ERB.new(File.read(@config[:views]+"/create_snapshot.erb")) + return response.result(binding), 200 + end + + # Deletes the specified snapshot. + # + # @param [Hash] params + # @option params [String] SnapshotId The ID of the Amazon EBS snapshot. + def delete_snapshot(params) + snapshot_id = params['SnapshotId'] + snapshot_id = snapshot_id.split('-')[1] + + snapshot = ImageEC2.new(Image.build_xml(snapshot_id.to_i), @client) + rc = snapshot.info + if OpenNebula::is_error?(rc) || !snapshot.ebs_snapshot? + rc ||= OpenNebula::Error.new() + rc.ec2_code = "InvalidSnapshot.NotFound" + return rc + end + + rc = snapshot.delete + if OpenNebula::is_error?(rc) + return rc + end + + erb_version = params['Version'] + + response = ERB.new(File.read(@config[:views]+"/delete_snapshot.erb")) + return response.result(binding), 200 + end + + def describe_snapshots(params) + impool = [] + params.each { |key, value| + if key =~ /SnapshotId\./ + if value =~ /snap\-(.+)/ + image = ImageEC2.new(Image.build_xml($1), @client) + rc = image.info + if OpenNebula.is_error?(rc) || !image.ebs_snapshot? + rc.ec2_code = "InvalidSnapshot.NotFound" + return rc + else + impool << image + end + else + rc = OpenNebula::Error.new("InvalidSnapshot.Malformed #{value}") + rc.ec2_code = "InvalidSnapshot.Malformed" + return rc + end + end + } + + if impool.empty? + user_flag = OpenNebula::Pool::INFO_ALL + impool = ImageEC2Pool.new(@client, user_flag) + + rc = impool.info + return rc if OpenNebula::is_error?(rc) + end + + erb_version = params['Version'] + + response = ERB.new(File.read(@config[:views]+"/describe_snapshots.erb")) + return response.result(binding), 200 + end end diff --git a/src/cloud/ec2/lib/econe_application.rb b/src/cloud/ec2/lib/econe_application.rb index 7360f720c3..c6de59116e 100644 --- a/src/cloud/ec2/lib/econe_application.rb +++ b/src/cloud/ec2/lib/econe_application.rb @@ -134,6 +134,20 @@ class EC2Application result,rc = econe_server.describe_regions(params) when 'DescribeAvailabilityZones' result,rc = econe_server.describe_availability_zones(params) + when 'CreateSnapshot' + result,rc = econe_server.create_snapshot(params) + when 'DeleteSnapshot' + result,rc = econe_server.delete_snapshot(params) + when 'DescribeSnapshots' + result,rc = econe_server.describe_snapshots(params) + when 'CreateTags' + result,rc = econe_server.create_tags(params) + when 'DeleteTags' + result,rc = econe_server.delete_tags(params) + when 'DescribeTags' + result,rc = econe_server.describe_tags(params) + #when 'CreateImage' + # result,rc = econe_server.create_image(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 index 5a49f739ae..6586969ffa 100644 --- a/src/cloud/ec2/lib/instance.rb +++ b/src/cloud/ec2/lib/instance.rb @@ -47,58 +47,147 @@ module Instance '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] + EC2_ACTIONS = { + :start => :resume, + :stop => :poweroff, + :terminate => :shutdown, + :reboot => :reboot + } - if instance_type != nil - path = @config[:template_location] + "/#{instance_type[:template]}" - end + # Include terminated instances in the describe_instances xml + DESCRIBE_WITH_TERMINATED_INSTANCES = true + # Terminated VMs will be included in the list + # till the termination date + TERMINATED_INSTANCES_EXPIRATION_TIME is reached + TERMINATED_INSTANCES_EXPIRATION_TIME = 900 + + def run_instances(params) + # Get the image + img = nil + if params['ImageId'] =~ /ami\-(.+)/ + img = $1 + else + rc = OpenNebula::Error.new("InvalidAMIID.Malformed #{params['ImageId']}") + rc.ec2_code = "InvalidAMIID.Malformed" + return rc end - # Get the image - tmp, img = params['ImageId'].split('-') + if @config[:use_file_templates] + # 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] - # 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'] - erb_vm_info[:public_key] = fetch_publickey(params) - erb_vm_info[:key_name] = params['KeyName'] - - template = ERB.new(File.read(erb_vm_info[:template])) - template_text = template.result(binding) - - erb_vms = Array.new - - min_count = params['MinCount'] || 1 - max_count = params['MaxCount'] || min_count - - max_count.to_i.times { - # Start the VM. - instance = VirtualMachine.new(VirtualMachine.build_xml, @client) - - rc = instance.allocate(template_text) - if OpenNebula::is_error?(rc) - if erb_vms.size < min_count.to_i - erb_vms.each { |vm| - vm.finalize - } - - return rc + if instance_type != nil + path = @config[:template_location] + "/#{instance_type[:template]}" end - else - instance.info - - erb_vms << instance end - } + + # 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'] + erb_vm_info[:public_key] = fetch_publickey(params) + erb_vm_info[:key_name] = params['KeyName'] + + template = ERB.new(File.read(erb_vm_info[:template])) + template_text = template.result(binding) + + erb_vms = Array.new + + min_count = params['MinCount'] || 1 + max_count = params['MaxCount'] || min_count + + max_count.to_i.times { + # Start the VM. + instance = VirtualMachine.new(VirtualMachine.build_xml, @client) + + rc = instance.allocate(template_text) + if OpenNebula::is_error?(rc) + if erb_vms.size < min_count.to_i + erb_vms.each { |vm| + vm.finalize + } + + return rc + end + else + instance.info + + erb_vms << instance + end + } + else + template_pool = TemplatePool.new(@client) + rc = template_pool.info + if OpenNebula::is_error?(rc) + return rc + end + + template_id = template_pool["VMTEMPLATE/TEMPLATE[EC2_INSTANCE_TYPE=\'#{params['InstanceType']}\']/../ID"] + if template_id.nil? + rc.ec2_code = "InvalidInstanceID.NotFound" + return rc + end + + template = Template.new(Template.build_xml(template_id), @client) + rc = template.info + if OpenNebula.is_error?(rc) + rc.ec2_code = "InvalidInstanceID.NotFound" + return rc + end + + merge_info = {} + merge_info["DISK"] = [] + merge_info["DISK"] << {"IMAGE_ID" => img.to_i} + + + template.each("TEMPLATE/DISK") { |e| + merge_info["DISK"] << e.to_hash["DISK"] + } + + merge_info["CONTEXT"] = {} + template.each("TEMPLATE/CONTEXT") { |e| + merge_info["CONTEXT"] = e.to_hash["CONTEXT"] + } + + context = merge_info["CONTEXT"] + + public_key = fetch_publickey(params) + context["EC2_PUBLIC_KEY"] = public_key if public_key + context["EC2_KEYNAME"] = params['KeyName'] if params['KeyName'] + context["EC2_USER_DATA"] = params['UserData'] if params['UserData'] + + template_str = template_to_str(merge_info) + vm_id = + + erb_vms = Array.new + + min_count = params['MinCount'] || 1 + max_count = params['MaxCount'] || min_count + + max_count.to_i.times { + # Start the VM. + rc = template.instantiate("", false, template_str) + if OpenNebula::is_error?(rc) + if erb_vms.size < min_count.to_i + erb_vms.each { |vm| + vm.finalize + } + + return rc + end + else + instance = VirtualMachine.new(VirtualMachine.build_xml(rc), @client) + instance.info + + erb_vms << instance + end + } + end erb_user_name = params['AWSAccessKeyId'] erb_version = params['Version'] @@ -108,11 +197,39 @@ module Instance end def describe_instances(params) - user_flag = OpenNebula::Pool::INFO_ALL - vmpool = VirtualMachinePool.new(@client, user_flag) + vmpool = [] + params.each { |key, value| + if key =~ /InstanceId\./ + if value =~ /i\-(.+)/ + vm = VirtualMachine.new(VirtualMachine.build_xml($1), @client) + rc = vm.info + if OpenNebula.is_error?(rc) + rc.ec2_code = "InvalidInstanceID.NotFound" + return rc + else + vmpool << vm + end + else + rc = OpenNebula::Error.new("InvalidInstanceID.Malformed #{value}") + rc.ec2_code = "InvalidInstanceID.Malformed" + return rc + end + end + } - rc = vmpool.info - return rc if OpenNebula::is_error?(rc) + if vmpool.empty? + user_flag = OpenNebula::Pool::INFO_ALL + vmpool = VirtualMachinePool.new(@client, user_flag) + + if include_terminated_instances? + rc = vmpool.info(user_flag, -1, -1, + OpenNebula::VirtualMachinePool::INFO_ALL_VM) + else + rc = vmpool.info + end + + return rc if OpenNebula::is_error?(rc) + end erb_version = params['Version'] erb_user_name = params['AWSAccessKeyId'] @@ -124,7 +241,7 @@ module Instance def terminate_instances(params) perform_action(params, "terminate_instances.erb") { |vm| if vm.status == 'runn' - vm.shutdown + vm.send(EC2_ACTIONS[:terminate]) else vm.finalize end @@ -133,19 +250,19 @@ module Instance def start_instances(params) perform_action(params, "start_instances.erb") { |vm| - vm.resume + vm.send(EC2_ACTIONS[:start]) } end def stop_instances(params) perform_action(params, "stop_instances.erb") { |vm| - vm.stop + vm.send(EC2_ACTIONS[:stop]) } end def reboot_instances(params) perform_action(params, "reboot_instances.erb") { |vm| - vm.reboot + vm.send(EC2_ACTIONS[:reboot]) } end @@ -171,6 +288,7 @@ module Instance rc = vm.info if OpenNebula::is_error?(rc) + rc.ec2_code = "InvalidInstanceID.NotFound" return rc end @@ -208,4 +326,68 @@ module Instance instance_id = "i-" + sprintf('%08i', vm.id) return "#{instance_id}" end + + def include_terminated_instances? + @config[:describe_with_terminated_instances] || DESCRIBE_WITH_TERMINATED_INSTANCES + end + + def include_terminated_instance?(vm) + if include_terminated_instances? + if EC2_STATES[ONE_STATES[vm.status]||:pending][:name] == "terminated" + if (Time.now.getutc.to_i - vm["ETIME"].to_i) <= TERMINATED_INSTANCES_EXPIRATION_TIME + return true + else + return false + end + end + end + + return true + end + + def template_to_str(attributes, indent=true) + if indent + ind_enter="\n" + ind_tab=' ' + else + ind_enter='' + ind_tab=' ' + end + + str=attributes.collect do |key, value| + if value + str_line="" + if value.class==Array && !value.empty? + value.each do |value2| + str_line << key.to_s.upcase << "=[" << ind_enter + if value2 && value2.class==Hash + str_line << value2.collect do |key3, value3| + str = ind_tab + key3.to_s.upcase + "=" + str += "\"#{value3.to_s}\"" if value3 + str + end.compact.join(",\n") + end + str_line << "\n]\n" + end + + elsif value.class==Hash && !value.empty? + str_line << key.to_s.upcase << "=[" << ind_enter + + str_line << value.collect do |key3, value3| + str = ind_tab + key3.to_s.upcase + "=" + str += "\"#{value3.to_s}\"" if value3 + str + end.compact.join(",\n") + + str_line << "\n]\n" + + else + str_line< former_tags.merge(tags)}) + + rc = resource.update(resource.template_like_str(template_key)) + return rc if OpenNebula::is_error?(rc) + } + + erb_version = params['Version'] + + response = ERB.new(File.read(@config[:views]+"/create_tags.erb")) + return response.result(binding), 200 + end + + # Deletes the specified set of tags from the specified set of resources. + # + # @param [Hash] params + # @option params [String] ResourceId.n The IDs of one or more resources + # to tag. For example, ami-1a2b3c4d. + # @option params [String] Tag.n.Key The key for a tag. + # @option params [String] Tag.n.Value The value for a tag. If you don't + # want the tag to have a value, specify the parameter with no value, + # and we set the value to an empty string. + # + # TODO: return if error or continue + def delete_tags(params) + resources = [] + tags = {} + + params.each { |key, value| + case key + when /ResourceId\./ + resources << case value + when /ami\-(.+)/ + image = ImageEC2.new(Image.build_xml($1), @client) + rc = image.info + if OpenNebula.is_error?(rc) || !image.ec2_ami? + rc ||= OpenNebula::Error.new() + rc.ec2_code = "InvalidAMIID.NotFound" + return rc + else + image + end + when /vol\-(.+)/ + image = ImageEC2.new(Image.build_xml($1), @client) + rc = image.info + if OpenNebula.is_error?(rc) || !image.ebs_volume? + rc ||= OpenNebula::Error.new() + rc.ec2_code = "InvalidVolume.NotFound" + return rc + else + image + end + when /snap\-(.+)/ + image = ImageEC2.new(Image.build_xml($1), @client) + rc = image.info + if OpenNebula.is_error?(rc) || !image.ebs_snapshot? + rc ||= OpenNebula::Error.new() + rc.ec2_code = "InvalidSnapshot.NotFound" + return rc + else + image + end + when /i\-(.+)/ + vm = VirtualMachine.new(VirtualMachine.build_xml($1), @client) + rc = vm.info + if OpenNebula.is_error?(rc) + rc.ec2_code = "InvalidInstanceID.NotFound" + return rc + else + vm + end + end + when /Tag\.(\d+)\.Key/ + tags[value] = params["Tag.#{$1}.Value"] || "" + end + } + + resources.each {|resource| + if resource.is_a?(VirtualMachine) + template_key = "USER_TEMPLATE" + elsif resource.is_a?(Image) + template_key = "TEMPLATE" + end + + tags.each { |key,value| + resource.delete_element("#{template_key}/EC2_TAGS/#{key}") + } + + rc = resource.update(resource.template_like_str(template_key)) + return rc if OpenNebula::is_error?(rc) + } + + erb_version = params['Version'] + + response = ERB.new(File.read(@config[:views]+"/delete_tags.erb")) + return response.result(binding), 200 + end + + def describe_tags(params) + user_flag = OpenNebula::Pool::INFO_ALL + impool = ImageEC2Pool.new(@client, user_flag) + + rc = impool.info + return rc if OpenNebula::is_error?(rc) + + user_flag = OpenNebula::Pool::INFO_ALL + vmpool = VirtualMachinePool.new(@client, user_flag) + + if include_terminated_instances? + rc = vmpool.info(user_flag, -1, -1, + OpenNebula::VirtualMachinePool::INFO_ALL_VM) + else + rc = vmpool.info + end + + return rc if OpenNebula::is_error?(rc) + + erb_version = params['Version'] + + response = ERB.new(File.read(@config[:views]+"/describe_tags.erb")) + return response.result(binding), 200 + end +end diff --git a/src/cloud/ec2/lib/views/create_image.erb b/src/cloud/ec2/lib/views/create_image.erb new file mode 100644 index 0000000000..480183aeb8 --- /dev/null +++ b/src/cloud/ec2/lib/views/create_image.erb @@ -0,0 +1,4 @@ + + <%= @request_id %> + ami-<%= sprintf('%08i', image_id) %> + diff --git a/src/cloud/ec2/lib/views/create_snapshot.erb b/src/cloud/ec2/lib/views/create_snapshot.erb new file mode 100644 index 0000000000..4cc5996b14 --- /dev/null +++ b/src/cloud/ec2/lib/views/create_snapshot.erb @@ -0,0 +1,11 @@ + + <%= @request_id %> + snap-<%= sprintf('%08i', snapshot["ID"]) %> + <%= params["VolumeId"] %> + <%= snapshot.render_state %> + <%= snapshot.render_create_time %> + 100% + <%= snapshot["UNAME"] %> + <%= snapshot.render_size %> + <%= snapshot["NAME"] %> + diff --git a/src/cloud/ec2/lib/views/create_tags.erb b/src/cloud/ec2/lib/views/create_tags.erb new file mode 100644 index 0000000000..9ab943b935 --- /dev/null +++ b/src/cloud/ec2/lib/views/create_tags.erb @@ -0,0 +1,4 @@ + + <%= @request_id %> + true + diff --git a/src/cloud/ec2/lib/views/create_volume.erb b/src/cloud/ec2/lib/views/create_volume.erb index c43a0b5471..f5ea114cec 100644 --- a/src/cloud/ec2/lib/views/create_volume.erb +++ b/src/cloud/ec2/lib/views/create_volume.erb @@ -1,10 +1,10 @@ - <%= @request_id %> - vol-<%= sprintf('%08i', image.id) %> - <%= image.render_size %> + <%= @request_id %> + <%= volume.ec2_id %> + <%= volume.render_size %> - <%= image.render_state %> - <%= image.render_create_time %> - \ No newline at end of file + <%= volume.render_state %> + <%= volume.render_create_time %> + diff --git a/src/cloud/ec2/lib/views/delete_snapshot.erb b/src/cloud/ec2/lib/views/delete_snapshot.erb new file mode 100644 index 0000000000..d18ed90271 --- /dev/null +++ b/src/cloud/ec2/lib/views/delete_snapshot.erb @@ -0,0 +1,4 @@ + + <%= @request_id %> + true + diff --git a/src/cloud/ec2/lib/views/delete_tags.erb b/src/cloud/ec2/lib/views/delete_tags.erb new file mode 100644 index 0000000000..468b087a44 --- /dev/null +++ b/src/cloud/ec2/lib/views/delete_tags.erb @@ -0,0 +1,4 @@ + + <%= @request_id %> + true + diff --git a/src/cloud/ec2/lib/views/describe_images.erb b/src/cloud/ec2/lib/views/describe_images.erb index 921ff139c5..f4fe95a0ee 100644 --- a/src/cloud/ec2/lib/views/describe_images.erb +++ b/src/cloud/ec2/lib/views/describe_images.erb @@ -3,7 +3,7 @@ <%= @request_id %> <% impool.each do |im| %> - <% if (state_image = im.render_state) && (im['TEMPLATE/EBS_VOLUME'] != 'YES') %> + <% if (state_image = im.render_state) && im.ec2_ami? %> <%= im['NAME'] %> <% if im['TEMPLATE/DESCRIPTION'] %> @@ -12,7 +12,7 @@ <% if im['TEMPLATE/PLATFORM'] %> <%= im['TEMPLATE/PLATFORM'] %> <% end %> - ami-<%= sprintf('%08i', im.id) %> + <%= im.ec2_id %> <%= im['SOURCE'].split('/').last %> <%= state_image %> <%= im['UNAME'] %> diff --git a/src/cloud/ec2/lib/views/describe_instances.erb b/src/cloud/ec2/lib/views/describe_instances.erb index bb8141a963..ef5d59cc20 100644 --- a/src/cloud/ec2/lib/views/describe_instances.erb +++ b/src/cloud/ec2/lib/views/describe_instances.erb @@ -12,6 +12,7 @@ <% vmpool.each do |vm| %> + <% if include_terminated_instance?(vm) %> <%= render_instance_id(vm) %> <%= vm['USER_TEMPLATE/IMAGE_ID'] %> @@ -45,6 +46,7 @@ <% end %> + <% end %> diff --git a/src/cloud/ec2/lib/views/describe_snapshots.erb b/src/cloud/ec2/lib/views/describe_snapshots.erb new file mode 100644 index 0000000000..4c4aa51504 --- /dev/null +++ b/src/cloud/ec2/lib/views/describe_snapshots.erb @@ -0,0 +1,23 @@ + + <%= @request_id %> + + <% impool.each do |im| %> + <% logger.debug(im.id) %> + <% if (state_image = im.render_state) && im.ebs_snapshot? %> + + snap-<%= sprintf('%08i', im.id) %> + + <%= state_image %> + <%= im.render_create_time %> + 100% + <%= im["UNAME"] %> + <%= im.render_size %> + <%= im["NAME"] %> + + + <% else + next + end + end %> + + diff --git a/src/cloud/ec2/lib/views/describe_tags.erb b/src/cloud/ec2/lib/views/describe_tags.erb new file mode 100644 index 0000000000..2933f6cc60 --- /dev/null +++ b/src/cloud/ec2/lib/views/describe_tags.erb @@ -0,0 +1,31 @@ + + <%= @request_id %> + + <% impool.each do |im| %> + <% if (state_image = im.render_state) && im.has_elements?("TEMPLATE/EC2_TAGS") %> + <% image_hash = im.to_hash %> + <% image_hash["IMAGE"]["TEMPLATE"]["EC2_TAGS"].each do |key, value| %> + + <%= im.ec2_id %> + image + <%= key %> + <%= value %> + + <% end + end + end %> + <% vmpool.each do |vm| %> + <% if include_terminated_instance?(vm) && vm.has_elements?("USER_TEMPLATE/EC2_TAGS") %> + <% vm_hash = vm.to_hash %> + <% vm_hash["VM"]["USER_TEMPLATE"]["EC2_TAGS"].each do |key, value| %> + + i-<%= sprintf('%08i', vm.id) %> + instance + <%= key %> + <%= value %> + + <% end + end + end %> + + diff --git a/src/cloud/ec2/lib/views/describe_volumes.erb b/src/cloud/ec2/lib/views/describe_volumes.erb index 77f935cb5f..778925574c 100644 --- a/src/cloud/ec2/lib/views/describe_volumes.erb +++ b/src/cloud/ec2/lib/views/describe_volumes.erb @@ -3,11 +3,11 @@ <%= @request_id %> <% impool.each do |im| %> - <% if (state_image = im.render_state) && (im['TEMPLATE/EBS_VOLUME'] == 'YES') %> + <% if (state_image = im.render_state) && im.ebs_volume? %> vol-<%= sprintf('%08i', im.id) %> <%= im.render_size %> - + <%= im["TEMPLATE/EBS_FROM_SNAPSHOT_ID"] %> <%= state_image %> <%= im.render_create_time %> @@ -30,4 +30,4 @@ end end %> - \ 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 f7306ff70c..e80e5a108a 100644 --- a/src/cloud/ec2/lib/views/run_instances.erb +++ b/src/cloud/ec2/lib/views/run_instances.erb @@ -11,7 +11,7 @@ <% erb_vms.each { |vm| %> <%= render_instance_id(vm) %> - <%= erb_vm_info[:ec2_img_id] %> + <%= params['ImageId'] %> <%= render_state(vm) %> <% if vm.has_elements?("TEMPLATE/NIC/IP") %> <% ips_str = vm.retrieve_elements("TEMPLATE/NIC/IP").join(', ') %> @@ -25,7 +25,7 @@ <%= vm['TEMPLATE/CONTEXT/EC2_KEYNAME'] %> <% end %> <%= vm.id %> - <%= erb_vm_info[:instance_type] %> + <%= params['InstanceType'] %> <%= render_launch_time(vm) %> default