Enhancing OpenNebula EC2 Driver - Part 3
01 January 2012
This is part 3 of the tutorial.
And now... the patch!
Once applied, the patch below will make the driver handle templates defined like:
REQUIREMENTS = "EC2REGION = \"eu-west-1\""
EC2 = [ AKI=aki-xxxxxxxx
AMI=ami-xxxxxxxx
ELASTICIP=eipalloc-xxxxxxxx
INSTANCETYPE=m1.small
KEYPAIR=<my keypair>
MONITORING=TRUE
PRIVATEIP=10.0.0.10
SECURITYGROUP=sg-xxxxxxxx
SUBNETID=subnet-xxxxxxxx
TAGS="Name=$NAME,
VMID=$VMID,
TEMPLATE_ID=$TEMPLATE_ID,
DESCRIPTION=\"My Description\",
CREATED_BY=$USERNAME"
VPCID=vpc-xxxxxxxx,
WAITFORINSTANCE=TRUE ]
Here is the batch. It has been successfully on OpenNebula 3.1.80 (pre-release of 3.2).
diff --git a/src/vmm_mad/ec2/one_vmm_ec2.rb b/src/vmm_mad/ec2/one_vmm_ec2.rb
index 78d078a..eb68440 100755
--- a/src/vmm_mad/ec2/one_vmm_ec2.rb
+++ b/src/vmm_mad/ec2/one_vmm_ec2.rb
@@ -52,8 +52,8 @@ class EC2Driver < VirtualMachineDriver
:terminate => "#{EC2_LOCATION}/bin/ec2-terminate-instances",
:describe => "#{EC2_LOCATION}/bin/ec2-describe-instances",
:associate => "#{EC2_LOCATION}/bin/ec2-associate-address",
- :reboot => "#{EC2_LOCATION}/bin/ec2-reboot-instances",
- :authorize => "#{EC2_LOCATION}/bin/ec2-authorize"
+ :authorize => "#{EC2_LOCATION}/bin/ec2-authorize",
+ :tags => "#{EC2_LOCATION}/bin/ec2-create-tags"
}
# EC2 constructor, loads defaults for the EC2Driver
@@ -134,11 +134,36 @@ class EC2Driver < VirtualMachineDriver
end
end
- ami = ec2_value(ec2,"AMI")
- keypair = ec2_value(ec2,"KEYPAIR")
- eip = ec2_value(ec2,"ELASTICIP")
- ports = ec2_value(ec2,"AUTHORIZEDPORTS")
- type = ec2_value(ec2,"INSTANCETYPE")
+ aki = ec2_value(ec2,"AKI")
+ ami = ec2_value(ec2,"AMI")
+ ports = ec2_value(ec2,"AUTHORIZEDPORTS")
+ blockmapping = ec2_value(ec2,"BLOCKDEVICEMAPPING")
+ clienttoken = ec2_value(ec2,"CLIENTTOKEN")
+ ec2region = host_info("deploy",host,"EC2REGION")
+ eip = ec2_value(ec2,"ELASTICIP")
+ type = ec2_value(ec2,"INSTANCETYPE")
+ keypair = ec2_value(ec2,"KEYPAIR")
+ licensepool = ec2_value(ec2,"LICENSEPOOL")
+ clustergroup = ec2_value(ec2,"PLACEMENTGROUP")
+ privateip = ec2_value(ec2,"PRIVATEIP")
+ ramdisk = ec2_value(ec2,"RAMDISK")
+ secgroup = ec2_value(ec2,"SECURITYGROUPS")
+ subnetid = ec2_value(ec2,"SUBNETID")
+ tags = ec2_value(ec2,"TAGS")
+ tenancy = ec2_value(ec2,"TENANCY")
+ vpcid = ec2_value(ec2,"VPCID")
+ waitb4eip = ec2_value(ec2,"WAITFORINSTANCE")
+ ec2context = ""
+
+ # get context data, if any
+ all_context_elements = xml.root.get_elements("CONTEXT")
+ context_root = all_context_elements[0]
+ if context_root
+ context_ud = ec2_value(context_root,"USERDATA")
+ context_udf = ec2_value(context_root,"USERDATAFILE")
+ ec2context << " -d '#{context_ud}'" if context_ud
+ ec2context << " -f #{context_udf}" if context_udf
+ end
if !ami
send_message(ACTION[:deploy],RESULT[:failure],id,
@@ -146,9 +171,26 @@ class EC2Driver < VirtualMachineDriver
return
end
- deploy_cmd = "#{EC2[:run]} #{ami}"
+ deploy_cmd = "#{EC2[:run]} --region #{ec2region} #{ec2context}"
+ deploy_cmd << " #{ami}"
+ deploy_cmd << " --kernel #{aki}" if aki
+ deploy_cmd << " -b #{blockmapping}" if blockmapping
+ deploy_cmd << " --client-token #{clienttoken}" if clienttoken
deploy_cmd << " -k #{keypair}" if keypair
+ deploy_cmd << " --license-pool #{licensepool}" if licensepool
deploy_cmd << " -t #{type}" if type
+ deploy_cmd << " --ramdisk #{ramdisk}" if ramdisk
+ deploy_cmd << " --placement-group #{clustergroup}" if clustergroup
+ if subnetid
+ deploy_cmd << " -s #{subnetid}"
+ deploy_cmd << " --private-ip-address #{privateip}" if privateip
+ deploy_cmd << " --tenancy #{tenancy}" if tenancy
+ end
+ if secgroup
+ for grouptok in secgroup.split(',')
+ deploy_cmd << " -g #{grouptok}"
+ end
+ end
deploy_exe = LocalCommand.run(deploy_cmd, log_method(id))
@@ -165,16 +207,71 @@ class EC2Driver < VirtualMachineDriver
deploy_id = $1
- if eip
- ip_cmd = "#{EC2[:associate]} #{eip} -i #{deploy_id}"
- ip_exe = LocalCommand.run(ip_cmd, log_method(id))
- end
-
if ports
- ports_cmd = "#{EC2[:authorize]} default -p #{ports}"
+ ports_cmd = "#{EC2[:authorize]} --region #{ec2region} default -p #{ports}"
ports_exe = LocalCommand.run(ports_cmd, log_method(id))
end
+ # adding EC2 tags if any are defined in the EC2 section.
+ # Tags should be defined as a TAG=VAL comma seperated string like
+ # TAGS="Tag1=Val1, Tag2=Val2, ..."
+ #
+ # Notes:
+ # - If 'Value' starts with a '$', the code will try to resolve it as
+ # a variable name. For example the special tag 'Name' can be use
+ # to define the name of the instance visible in the AWS Console
+ # with the following tag definition
+ # TAGS="Name=$NAME, tag2=val2, ..."
+ #
+ # To resolve the variables, the instance's deployment.0 file
+ # is parsed as follow:
+ # -> try to find an element corresponding to the variable name at
+ # the root of the xml tree
+ # -> if none is found, another search if tried at second level of
+ # the tree
+ # -> In both cases, the first match will be used so you know what
+ # to look at if you don't get what you expect... might be
+ # fixed in the future if needed.
+ if tags
+ tags_cmd = "#{EC2[:tags]} --region #{ec2region} #{deploy_id}"
+ for tag in tags.split(',')
+ token = tag.split('=')
+ t_regex = /^(.{1})(.*)$/
+ t_match = t_regex.match(token[1])
+ if t_match[1] == "$"
+ value = ""
+ element = xml.root.elements[t_match[2]]
+ element = xml.root.elements["*/" << t_match[2]] unless element
+ value = element.text.strip if element && element.text
+ tag = token[0].strip << "=" << value.chomp
+ end
+ tags_cmd << " -t #{tag}"
+ end
+ tags_exe = LocalCommand.run(tags_cmd, log_method(id))
+ end
+
+ if eip
+ if subnetid
+ ip_cmd = "#{EC2[:associate]} --region #{ec2region} -a #{eip} -i #{deploy_id}"
+ else
+ ip_cmd = "#{EC2[:associate]} --region #{ec2region} #{eip} -i #{deploy_id}"
+ end
+
+ # Make sure instance is running state before assigning Elastic IP
+ if waitb4eip
+ pos=2
+ pos=1 if subnetid
+ wait4instance(ec2region,id,deploy_id,pos,"running")
+ end
+
+ ip_exe = LocalCommand.run(ip_cmd, log_method(id))
+ if !ip_exe.stdout.match(/^ADDRESS\s*(.+?)\s/)
+ send_message(ACTION[:deploy],RESULT[:failure],id,
+ "Could not associate Elastic IP. Check template definition.")
+ return
+ end
+ end
+
send_message(ACTION[:deploy],RESULT[:success],id,deploy_id)
end
@@ -185,12 +282,14 @@ class EC2Driver < VirtualMachineDriver
host = msg.elements["HOST"].text
deploy_id = msg.elements["DEPLOY_ID"].text
- ec2_terminate(ACTION[:shutdown], id, deploy_id)
+ ec2_terminate(ACTION[:shutdown], id, deploy_id, host)
end
-
- # Reboot a EC2 instance
+
+ # Reboot a EC2 instance
def reboot(id, drv_message)
- cmd = "#{EC2_LOCATION}/bin/ec2-reboot-instances #{deploy_id}"
+ ec2region = host_info("reboot",host,"EC2REGION")
+
+ cmd = "#{EC2_LOCATION}/bin/ec2-reboot-instances --region #{ec2region} #{deploy_id}"
exe = LocalCommand.run(cmd, log_method(id))
if exe.code != 0
@@ -209,7 +308,7 @@ class EC2Driver < VirtualMachineDriver
host = msg.elements["HOST"].text
deploy_id = msg.elements["DEPLOY_ID"].text
- ec2_terminate(ACTION[:cancel], id, deploy_id)
+ ec2_terminate(ACTION[:cancel], id, deploy_id, host)
end
# Get info (IP, and state) for a EC2 instance
@@ -218,13 +317,14 @@ class EC2Driver < VirtualMachineDriver
host = msg.elements["HOST"].text
deploy_id = msg.elements["DEPLOY_ID"].text
+ ec2region = host_info("poll",host,"EC2REGION")
info = "#{POLL_ATTRIBUTE[:usedmemory]}=0 " \
"#{POLL_ATTRIBUTE[:usedcpu]}=0 " \
"#{POLL_ATTRIBUTE[:nettx]}=0 " \
"#{POLL_ATTRIBUTE[:netrx]}=0"
- cmd = "#{EC2[:describe]} #{deploy_id}"
+ cmd = "#{EC2[:describe]} --region #{ec2region} --hide-tags #{deploy_id}"
exe = LocalCommand.run(cmd, log_method(id))
if exe.code != 0
@@ -255,8 +355,10 @@ class EC2Driver < VirtualMachineDriver
private
- def ec2_terminate(action, id, deploy_id)
- cmd = "#{EC2_LOCATION}/bin/ec2-terminate-instances #{deploy_id}"
+ def ec2_terminate(action, id, deploy_id, host)
+ ec2region = host_info("terminate",host,"EC2REGION")
+
+ cmd = "#{EC2_LOCATION}/bin/ec2-terminate-instances --region #{ec2region} #{deploy_id}"
exe = LocalCommand.run(cmd, log_method(id))
if exe.code != 0
@@ -279,6 +381,48 @@ private
return value
end
+ def host_info(action,host,key)
+ # get EC2REGION parameter from the selected host
+ query_cmd = "#{ONE_LOCATION}/bin/onehost show #{host}"
+ query_exe = LocalCommand.run(query_cmd, log_method(id))
+
+ if query_exe.code != 0
+ send_message(action,RESULT[:failure],host)
+ return
+ end
+
+ if !query_exe.stdout.match(/^#{key}=(.+?)\s/)
+ send_message(action,RESULT[:failure],host,
+ "Could not find #{param} parameter for host #{host}. Use onehost update #{host} to define this paramete first")
+ return
+ end
+
+ return $1
+ end
+ def wait4instance(region,id,deploy_id,pos,value)
+ found = 0
+ while found == 0 do
+ poll_cmd = "#{EC2[:describe]} --region #{region} --hide-tags #{deploy_id}"
+ poll_exe = LocalCommand.run(poll_cmd, log_method(id))
+
+ if poll_exe.code != 0
+ send_message(ACTION[:deploy],RESULT[:failure],id,
+ "Error polling instance.")
+ return
+ end
+
+ poll_exe.stdout.match(Regexp.new("INSTANCE\\s+#{deploy_id}\\s+(.+)"))
+
+ if !$1
+ send_message(ACTION[:deploy],RESULT[:failure],id,
+ "ERROR: Instance not found and should have been created.")
+ return
+ else
+ monitor_data = $1.split(/\s+/)
+ found = 1 if monitor_data[pos] == value
+ end
+ end
+ end
end
# EC2Driver Main program
blog comments powered by Disqus