diff --git a/src/ozones/Server/lib/OZones/VDC.rb b/src/ozones/Server/lib/OZones/VDC.rb index bdc23b1ae7..9e51df6da4 100644 --- a/src/ozones/Server/lib/OZones/VDC.rb +++ b/src/ozones/Server/lib/OZones/VDC.rb @@ -25,6 +25,7 @@ module OZones property :name, String, :required => true, :unique => true property :group_id, Integer property :vdcadminname, String, :required => true + property :vdcadmin_id, Integer property :acls, String property :hosts, String @@ -46,52 +47,6 @@ module OZones return vdc_attributes end - ####################################################################### - # Methods to handle the ACL list - ####################################################################### - # The ID of the first host ACL - HOST_ACL_FIRST_ID = 3 - - # This method returns an Array of ACL strings to create them - # in the target zone - def get_vdc_acls_str(user_id, group_id) - rule_str = Array.new - - # Grant permissions to the group - rule_str << "@#{group_id} VM+NET+IMAGE+TEMPLATE/* " \ - "CREATE+INFO_POOL_MINE" - - # Grant permissions to the vdc admin - rule_str << "##{user_id} USER/* CREATE" - rule_str << "##{user_id} USER/@#{group_id} MANAGE+DELETE+INFO" - - ############################################################### - #When more rules are added the class constant HOST_ACL_FIRST_ID - #must be modified - ############################################################### - - rule_str.concat(self.get_host_acls_str(group_id)) - end - - def get_host_acls_str(group_id, host_list = nil) - rule_str = Array.new - - if host_list == nil - host_list = self.hosts - end - - # Grant permissions to use the vdc hosts - host_list.split(',').each{|hostid| - rule_str << "@#{group_id} HOST/##{hostid} USE" - } - - return rule_str - end - - def get_host_acls - self.acls.split(',')[HOST_ACL_FIRST_ID..-1].collect!{|x| x.to_i} - end - # Returns the host acls as an array of strings. # The acls of the VDC are updated and the host acl ids removed def get_host_acls!(new_host_acls = nil) @@ -111,4 +66,246 @@ module OZones return acl_ids end end + + ########################################################################### + # This class represents a VDC able to interact with its supporting + # OpenNebula installation through OCA. Data persistence is provided by a + # Vdc class + ########################################################################### + class OpenNebulaVdc + ####################################################################### + # Constants + ####################################################################### + VDC_ATTRS = [:vdcadminname, :vdcadminpass, :name, :hosts] + + attr_reader :vdc + + #Creates an OpenNebula VDC, using its ID, vdcid and the associated zone + def initialize(vdcid, zone = nil) + if vdcid != -1 + @vdc = Vdc.get(vdcid) + + if !@vdc + raise "Error: VDC with id #{vdcid} not found" + end + + zone = OZones::Zones.get(@vdc.zones_id) + end + + @client = OpenNebula::Client.new( + "#{zone.onename}:#{zone.onepass}", + zone.endpoint, + false) + end + + ####################################################################### + # + ####################################################################### + def create(vdc_data) + #Check and prepare VDC data + VDC_ATTRS.each { |param| + if !vdc_data[param] + return OZones::Error.new("Error: Couldn't create vdc." \ + "Mandatory attribute '#{param}' is missing.") + end + } + + #Create a vdc record + @vdc = Vdc.new + @vdc.raise_on_save_failure = true + + vdcpass = Digest::SHA1.hexdigest(vdc_data.delete(:vdcadminpass)) + @vdc.attributes = vdc_data + puts vdc_data + + # Create a group in the zone with the VDC name + group = OpenNebula::Group.new(OpenNebula::Group.build_xml, @client) + rc = group.allocate(@vdc.name) + + return rc if OpenNebula.is_error?(rc) + + @vdc.group_id = group.id + + # Create the VDC admin user in the Zone + user = OpenNebula::User.new(OpenNebula::User.build_xml, @client) + rc = user.allocate(@vdc.vdcadminname, vdcpass) + + return rollback(group, nil, nil, rc) if OpenNebula.is_error?(rc) + + @vdc.vdcadmin_id = user.id + + # Change primary group of the admin user to the VDC group + rc = user.chgrp(group.id) + return rollback(group, user, nil, rc) if OpenNebula.is_error?(rc) + + # Add ACLs + aclp = OpenNebula::AclPool.new(@client) + rules = get_acls + + acls_str = "" + + rules.each{ |rule_str| + acl = OpenNebula::Acl.new(OpenNebula::Acl.build_xml,@client) + rc = acl.allocate(*OpenNebula::Acl.parse_rule(rule_str)) + + if OpenNebula.is_error?(rc) + return rollback(group, user, acls_str, rc) + end + + acls_str << acl.id.to_s << "," + } + + @vdc.acls = acls_str.chop + + return true + end + + def destroy + # Delete the resources from the VDC + delete_images + delete_templates + delete_vms + delete_vns + delete_acls + + # Delete users from a group + up = OpenNebula::UserPool.new(@client) + up.info + + up.each{|user| + if user['GID'].to_i == @vdc.group_id + user.delete + end + } + + # Delete the group + rc = OpenNebula::Group.new_with_id(@vdc.group_id, @client).delete + + if OpenNebula.is_error?(rc) + return rc + else + return @vdc.destroy + end + end + + private + ####################################################################### + # Functions to generate ACL Strings + ####################################################################### + # The ID of the first host ACL + HOST_ACL_FIRST_ID = 3 + + # This method returns an Array of ACL strings to create them + # in the target zone + def get_acls + rule_str = Array.new + + # Grant permissions to the group + rule_str << "@#{@vdc.group_id} VM+NET+IMAGE+TEMPLATE/* " \ + "CREATE+INFO_POOL_MINE" + + # Grant permissions to the vdc admin + rule_str << "##{@vdc.vdcadmin_id} USER/* CREATE" + rule_str << "##{@vdc.vdcadmin_id} USER/@#{@vdc.group_id} " \ + "MANAGE+DELETE+INFO" + + ############################################################### + #When more rules are added the class constant HOST_ACL_FIRST_ID + #must be modified + ############################################################### + + rule_str.concat(get_host_acls) + end + + def get_host_acls(host_list = nil) + rule_str = Array.new + + if host_list == nil + host_list = @vdc.hosts + end + + # Grant permissions to use the vdc hosts + host_list.split(',').each{|hostid| + rule_str << "@#{@vdc.group_id} HOST/##{hostid} USE" + } + + return rule_str + end + + ####################################################################### + # Functions to delete resources associated to the VDC + ####################################################################### + # Deletes ACLs for the hosts + def delete_host_acls + @vdc.acls.split(',')[HOST_ACL_FIRST_ID..-1].each{|acl| + OpenNebula::Acl.new_with_id(acl.to_i, @client).delete + } + end + + # Delete ACLs + def delete_acls + @vdc.acls.split(",").each{|acl| + OpenNebula::Acl.new_with_id(acl.to_i, @client).delete + } + end + + # Deletes images + def delete_images + ip = OpenNebula::ImagePool.new(@client) + ip.info + + ip.each{|image| + image.delete if image['GID'].to_i == @vdc.group_id + } + end + + # Deletes templates + def delete_templates + tp = OpenNebula::TemplatePool.new(@client) + tp.info + + tp.each{|template| + template.delete if template['GID'].to_i == @vdc.group_id + } + end + + # Deletes VMs + def delete_vms + vmp = OpenNebula::VirtualMachinePool.new(@client) + vmp.info + + vmp.each{|vm| + vm.delete if vm['GID'].to_i == @vdc.group_id + } + end + + # Deletes VNs + def delete_vns + vnp = OpenNebula::VirtualNetworkPool.new(@client) + vnp.info + + vnp.each{|vn| + vnp.delete if vn['GID'].to_i == @vdc.group_id + } + end + + ####################################################################### + # Misc helper functions for the class + ####################################################################### + + # Deletes resources from failed created VDC + def rollback(group, user, acls, rc) + group.delete + user.delete if user + + if acls + acls.chop + acls.split(",").each{|acl| + OpenNebula::Acl.new_with_id(acl.to_i, @client).delete + } + end + + return rc + end + end end diff --git a/src/ozones/Server/lib/OZones/Zones.rb b/src/ozones/Server/lib/OZones/Zones.rb index 528ab3dfa3..59e8878005 100644 --- a/src/ozones/Server/lib/OZones/Zones.rb +++ b/src/ozones/Server/lib/OZones/Zones.rb @@ -109,7 +109,7 @@ module OZones end ########################################################################### - # This class represents a Zone with able to interact with its supporting + # This class represents a Zone able to interact with its supporting # OpenNebula installation through OCA. Data persistence is provided by a # Zones class ########################################################################## @@ -118,7 +118,7 @@ module OZones @zone = Zones.get(zoneid) if !@zone - raise "Error: Resource zone with id #{zoneid} not found" + raise "Error: Zone with id #{zoneid} not found" end @client = OpenNebula::Client.new( diff --git a/src/ozones/Server/models/OzonesServer.rb b/src/ozones/Server/models/OzonesServer.rb index 11210ac54f..eb5e200791 100644 --- a/src/ozones/Server/models/OzonesServer.rb +++ b/src/ozones/Server/models/OzonesServer.rb @@ -80,13 +80,18 @@ class OzonesServer # Get a json representation resource with local (DB) info def get_resource(kind, id) - resource = retrieve_resource(kind, id) - - if OZones.is_error?(resource) - return [404, resource.to_json] - else - return [200, resource.to_json] + rc = 200 + res = case kind + when "vdc" then + OZones::Vdc.get(id) + when "zone" then + OZones::Zones.get(id) + else + rc = 404 + OZones::Error.new("Error: #{kind} resource not supported") end + + return [rc, res.to_json] end ############################################################################ @@ -106,78 +111,53 @@ class OzonesServer data.each{|key,value| vdc_data[key.downcase.to_sym]=value if key!="pool" } + + #Get the Zone that will host the VDC. And check resouces + zoneid = vdc_data.delete(:zoneid) + force = vdc_data.delete(:force) - mandatory_params = [:vdcadminname, :vdcadminpass, - :zoneid, :name, :hosts] + if !zoneid + return [400, OZones::Error.new("Error: Couldn't create " \ + "vdc. Mandatory attribute zoneid missing.").to_json] + end - mandatory_params.each { |param| - if !vdc_data[param] - return [400, OZones::Error.new( - "Error: Couldn't create resource #{kind}. " + - "Mandatory attribute '#{param}' is missing.").to_json] - end - } - - # Check if the referenced zone exists - zone=OZones::Zones.get(vdc_data[:zoneid]) + zone = OZones::Zones.get(zoneid) if !zone - error = OZones::Error.new("Error: Zone " + - "#{vdc_data[:zoneid]} not found, cannot create Vdc.") - return [404, error.to_json] + return [404, OZones::Error.new("Error: Couldn't create " \ + "vdc. Zone #{zoneid} not found.").to_json] end - if (!vdc_data[:force] or vdc_data[:force].upcase!="YES") and + if (!force or force.upcase!="YES") and !host_uniqueness?(zone, vdc_data[:hosts]) - return [403, OZones::Error.new( - "Error: Couldn't create resource #{kind}. " + - "Hosts are not unique, and no force option" + - " was given.").to_json] + + return [403, OZones::Error.new( "Error: Couldn't create " \ + "Hosts are not unique, use force to override").to_json] end - vdcadminname = vdc_data[:vdcadminname] - vdcadminpass = vdc_data[:vdcadminpass] - vdc_data.delete(:zoneid) - vdc_data.delete(:vdcadminpass) - vdc_data.delete(:force) + # Create de VDC + vdc = OZones::OpenNebulaVdc.new(-1,zone) + rc = vdc.create(vdc_data) - begin - vdc = OZones::Vdc.create(vdc_data) - rescue Exception => e - msg = e.message - msg["accessible in OZones::Vdc"] = "supported." - return [400, OZones::Error.new( - "Error: Couldn't create resource #{kind}." + - " #{msg}").to_json] + if OpenNebula.is_error?(rc) + return [400, OZones::Error.new("Error: Couldn't create " \ + "vdc. Reason: #{rc.message}").to_json] end - zone.vdcs << vdc - zone.save + #Update the zone and save the vdc + zone.raise_on_save_failure = true + zone.vdcs << vdc.vdc - if zone.saved? and vdc.saved? - vdcadminpass = Digest::SHA1.hexdigest(vdcadminpass) - rc = @ocaInt.create_vdc_in_zone(zone, - vdc, - vdcadminname, - vdcadminpass) - if OpenNebula.is_error?(rc) - vdc.destroy - return [400, OZones::Error.new( - "Error: Couldn't create #{kind}. Reason: " + - rc.message).to_json] - else - vdc.acls = rc[0] - vdc.group_id = rc[1] - vdc.save - - pr.update # Rewrite proxy conf file - return [200, vdc.to_json] - end - else - return [400, OZones::Error.new( - "Error: Couldn't create resource #{kind}." + - " Maybe duplicated name?").to_json] + begin + zone.save + rescue => e + #TODO Rollback VDC creation? + return [400, OZones::Error.new("Error: Couldn't create " \ + "vdc. Zone could not be saved: #{e.message}").to_json] end + pr.update # Rewrite proxy conf file + return [200, vdc.to_json] + when "zone" then zone = OZones::Zones.create(data) @@ -279,21 +259,30 @@ class OzonesServer ############################################################################ # Deletes a resource of a kind, and updates the Proxy Rules def delete_resource(kind, id, pr) - resource = retrieve_resource(kind, id) - if OZones.is_error?(resource) - return [404, resource.to_json] + case kind + when "vdc" then + begin + vdc = OZones::OpenNebulaVdc.new(id) + rc = vdc.destroy + rescue => e + return [404, OZones::Error.new("Error: Can not delete " \ + "vdc. Reason: #{e.message}").to_json] + end + when "zone" then + zone = OZones::Zones.get(id) + + if zone + rc = zone.destroy + else + return [404, OZones::Error.new("Error: Can not delete " \ + "zone. Reason: zone #{id} not found").to_json] + end + else + return [404, OZones::Error.new("Error: #{kind} resource " \ + "not supported").to_json] end - if kind == "vdc" - rc = @ocaInt.delete_vdc_in_zone(id) - if OpenNebula.is_error?(rc) - return [500, OZones::Error.new( - "Error: Couldn't delete resources from VDC with id #{id}, " + - "aborting VDC deletion. Reason:" + rc.message).to_json] - end - end - - if !resource.destroy + if !rc return [500, OZones::Error.new( "Error: Couldn't delete resource #{kind} with id #{id}").to_json] else @@ -303,6 +292,9 @@ class OzonesServer end end + ############################################################################ + # Misc Helper Functions + ############################################################################ private # Check if hosts are already include in any Vdc of the zone @@ -322,23 +314,4 @@ class OzonesServer return true end - - # Get hold of a object of a particular kind - def retrieve_resource(kind, id) - rc = case kind - when "vdc" then - OZones::Vdc.get(id) - when "zone" then - OZones::Zones.get(id) - else - OZones::Error.new("Error: #{kind} resource not supported") - end - - if rc - return rc - else - return OZones::Error.new("Error: #{kind} with id #{id} not found") - end - end - end