diff --git a/src/cloud/common/CloudServer.rb b/src/cloud/common/CloudServer.rb index 9e3883ad1a..d092e5c4fc 100755 --- a/src/cloud/common/CloudServer.rb +++ b/src/cloud/common/CloudServer.rb @@ -37,7 +37,7 @@ class CloudServer ########################################################################## # Public attributes ########################################################################## - attr_reader :config, :logger + attr_reader :config # Initializes the Cloud server based on a config file # config_file:: _String_ for the server. MUST include the following @@ -47,9 +47,18 @@ class CloudServer # XMLRPC def initialize(config, logger=nil) # --- Load the Cloud Server configuration file --- - @config = config - @logger = logger + @config = config + @@logger = logger end + + def self.logger + return @@logger + end + + def logger + return @@logger + end + # # Prints the configuration of the server # diff --git a/src/ozones/Client/bin/onevdc b/src/ozones/Client/bin/onevdc index 1fb688a2b2..9820efff68 100755 --- a/src/ozones/Client/bin/onevdc +++ b/src/ozones/Client/bin/onevdc @@ -43,13 +43,6 @@ cmd=CommandParser::CmdParser.new(ARGV) do ######################################################################## set :option, CommandParser::OPTIONS - FORCE={ - :name => "force", - :short => "-f", - :large => "--force", - :description => "Force the usage of Hosts in more than one VDC" - } - begin helper = VDCHelper.new "vdc" rescue Exception => e @@ -57,7 +50,7 @@ cmd=CommandParser::CmdParser.new(ARGV) do exit -1 end - command :create, 'Create a new VDC', :file, :options=>[FORCE] do + command :create, 'Create a new VDC', :file do helper.create_resource(args[0], options) end @@ -74,8 +67,7 @@ cmd=CommandParser::CmdParser.new(ARGV) do helper.delete_resource(args[0],options) end - command :addhost, 'Adds the set of hosts to the VDC', - :vdcid, :range, :options=>[FORCE] do + command :addhost, 'Adds the set of hosts to the VDC', :vdcid, :range do helper.addhost(args[0], args[1], options) end diff --git a/src/ozones/Client/lib/cli/ozones_helper/vdc_helper.rb b/src/ozones/Client/lib/cli/ozones_helper/vdc_helper.rb index cf0970e24c..44ae9e884f 100644 --- a/src/ozones/Client/lib/cli/ozones_helper/vdc_helper.rb +++ b/src/ozones/Client/lib/cli/ozones_helper/vdc_helper.rb @@ -17,7 +17,14 @@ require 'cli/ozones_helper' require 'cli/one_helper' +require 'zona' + class VDCHelper < OZonesHelper::OZHelper + NAME_REG = /[\w\d_-]+/ + VAR_REG = /\s*(#{NAME_REG})\s*=\s*/ + + SVAR_REG = /^#{VAR_REG}([^\[]+?)(#.*)?$/ + def initialize(kind, user=nil, pass=nil, endpoint_str=nil, timeout=nil, debug_flag=true) @vdc_str = kind @@ -25,13 +32,27 @@ class VDCHelper < OZonesHelper::OZHelper end def create_resource(template, options) - tmpl_str = File.read(template) + tmpl_str = File.read(template) + tmpl_hash = Hash.new - if options[:force] - tmpl_str << "FORCE=YES\n" + tmpl_str.scan(SVAR_REG) do | m | + key = m[0].strip.upcase + value = m[1].strip + + tmpl_hash[key] = value end - rc = @client.post_resource_str(@vdc_str, tmpl_str) + hosts = tmpl_hash.delete("HOSTS") + ds = tmpl_hash.delete("DATASTORES") + nets = tmpl_hash.delete("NETWORKS") + + tmpl_hash["RESOURCES"] = { "HOSTS" => eval("[#{hosts}]"), + "DATASTORES" => eval("[#{ds}]"), + "NETWORKS" => eval("[#{nets}]") } + + vdc = { "#{@vdc_str.upcase}" => tmpl_hash } + + rc = @client.post_resource(@vdc_str,Zona::OZonesJSON.to_json(vdc)) if Zona::is_error?(rc) [-1, rc.message] @@ -108,16 +129,19 @@ class VDCHelper < OZonesHelper::OZHelper def format_resource(vdc, options) str_h1="%-60s" - str="%-10s: %-20s" + str="%-12s: %-20s" CLIHelper.print_header(str_h1 % ["VDC #{vdc['name']} INFORMATION"]) - puts str % ["ID ", vdc[:ID].to_s] - puts str % ["NAME ", vdc[:NAME].to_s] - puts str % ["GROUP_ID ", vdc[:GROUP_ID].to_s] - puts str % ["ZONEID ", vdc[:ZONES_ID].to_s] - puts str % ["VDCADMIN ", vdc[:VDCADMINNAME].to_s] - puts str % ["HOST IDs ", vdc[:HOSTS].to_s] + puts str % ["ID ", vdc[:ID].to_s] + puts str % ["NAME ", vdc[:NAME].to_s] + puts str % ["ZONE_ID ", vdc[:ZONES_ID].to_s] + puts str % ["CLUSTER_ID ", vdc[:CLUSTER_ID].to_s] + puts str % ["GROUP_ID ", vdc[:GROUP_ID].to_s] + puts str % ["VDCADMIN ", vdc[:VDCADMINNAME].to_s] + puts str % ["HOSTS ", vdc[:RESOURCES][:HOSTS].to_s] + puts str % ["DATASTORES ", vdc[:RESOURCES][:DATASTORES].to_s] + puts str % ["NETWORKS ", vdc[:RESOURCES][:NETWORKS].to_s] puts return 0 diff --git a/src/ozones/Client/lib/zona.rb b/src/ozones/Client/lib/zona.rb index d22a8270c4..fcec4978f3 100644 --- a/src/ozones/Client/lib/zona.rb +++ b/src/ozones/Client/lib/zona.rb @@ -224,12 +224,8 @@ EOT return Client.parse_error(res, kind) end - - private - - # Starts an http connection and calls the block provided. SSL flag # is set if needed. def self.http_start(url, timeout, &block) @@ -296,7 +292,6 @@ EOT end - # Parses a OpenNebula template string and turns it into a JSON string # @param [String] kind element kind # @param [String] tmpl_str template diff --git a/src/ozones/Server/lib/OZones/VDC.rb b/src/ozones/Server/lib/OZones/VDC.rb index b3b89bb905..e090699497 100644 --- a/src/ozones/Server/lib/OZones/VDC.rb +++ b/src/ozones/Server/lib/OZones/VDC.rb @@ -14,49 +14,78 @@ # limitations under the License. # #--------------------------------------------------------------------------- # +require 'OzonesServer' + module OZones + # VDC class represents a virtual datacenter abstraction. It is defined by an + # ID, NAME, the GROUP backing the VDC, the admin credentials and the VDC + # resources. VDC resources are stored in JSON document in the DB and used as + # a hash in the VDC. class Vdc include DataMapper::Resource include OpenNebulaJSON::JSONUtils - extend OpenNebulaJSON::JSONUtils + extend OpenNebulaJSON::JSONUtils property :ID, Serial property :NAME, String, :required => true, :unique => true property :GROUP_ID, Integer property :VDCADMINNAME, String, :required => true property :VDCADMIN_ID, Integer - property :ACLS, Text - property :HOSTS, Text + property :CLUSTER_ID, Integer + property :RESOURCES, Text belongs_to :zones + def resources + rsrc_json = self.RESOURCES + + parser = JSON.parser.new(rsrc_json, {:symbolize_names => true}) + parser.parse + end + + def resources=(rsrc_hash) + self.RESOURCES = JSON.generate(rsrc_hash) + end + def self.to_hash zonePoolHash = Hash.new - zonePoolHash["VDC_POOL"] = Hash.new + + zonePoolHash["VDC_POOL"] = Hash.new zonePoolHash["VDC_POOL"]["VDC"] = Array.new unless self.all.empty? - self.all.each{|vdc| - # Hack! zones_ID does not respect the - # "all capital letters" policy + + self.all.each{ |vdc| + # Hack! zones_ID does not respect the "all capital letters" policy attrs = vdc.attributes.clone + attrs[:ZONES_ID] = vdc.attributes[:zones_ID] attrs.delete(:zones_ID) + rsrc_json = attrs.delete(:RESOURCES) + parser = JSON.parser.new(rsrc_json, {:symbolize_names=>true}) + attrs[:RESOURCES] = parser.parse + zonePoolHash["VDC_POOL"]["VDC"] << attrs } + return zonePoolHash end def to_hash vdc_attributes = Hash.new - # Hack! zones_ID does not respect the - # "all capital letters" policy + # Hack! zones_ID does not respect the "all capital letters" policy attrs = attributes.clone + attrs[:ZONES_ID] = attributes[:zones_ID] attrs.delete(:zones_ID) + rsrc_json = attrs.delete(:RESOURCES) + parser = JSON.parser.new(rsrc_json, {:symbolize_names=>true}) + attrs[:RESOURCES] = parser.parse + vdc_attributes["VDC"] = attrs + return vdc_attributes end end @@ -70,14 +99,17 @@ module OZones ####################################################################### # Constants ####################################################################### - VDC_ATTRS = [:VDCADMINNAME, :VDCADMINPASS, :NAME, :HOSTS] + VDC_ATTRS = [:VDCADMINNAME, + :VDCADMINPASS, + :NAME, + :CLUSTER_ID, + :RESOURCES] attr_reader :vdc attr_reader :zone #Creates an OpenNebula VDC, using its ID, vdcid and the associated zone def initialize(vdcid, zone = nil) - if vdcid != -1 @vdc = Vdc.get(vdcid) @@ -102,21 +134,29 @@ module OZones # ####################################################################### def create(vdc_data) - #Check and prepare VDC data + OzonesServer::logger.debug {"Creating new VDC #{vdc_data}"} + + #Check and prepare VDC data and preserve RESOURCES VDC_ATTRS.each { |param| if !vdc_data[param] return OZones::Error.new("Error: Couldn't create vdc." \ - "Mandatory attribute '#{param}' is missing.") + "Mandatory attribute '#{param}' is missing.") end } - #Create a vdc record + rsrc = vdc_data.delete(:RESOURCES) + vdcpass = vdc_data.delete(:VDCADMINPASS) + + #------------------------------------------------------------------- + # Create a vdc record + #------------------------------------------------------------------- @vdc = Vdc.new - vdcpass = vdc_data.delete(:VDCADMINPASS) @vdc.attributes = 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) @@ -124,7 +164,11 @@ module OZones @vdc.GROUP_ID = group.id + OzonesServer::logger.debug {"Group #{group.id} created"} + + #------------------------------------------------------------------- # Create the VDC admin user in the Zone + #------------------------------------------------------------------- user = OpenNebula::User.new(OpenNebula::User.build_xml, @client) rc = user.allocate(@vdc.VDCADMINNAME, vdcpass) @@ -132,31 +176,44 @@ module OZones @vdc.VDCADMIN_ID = user.id + OzonesServer::logger.debug {"VDC admin user #{user.id} created"} + + #------------------------------------------------------------------- # 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 + #------------------------------------------------------------------- + rules = get_acls(rsrc) - rc, acls_str = create_acls(rules) - return rollback(group, user,acls_str,rc) if OpenNebula.is_error?(rc) + OzonesServer::logger.debug {"Creating ACLs #{rules}..."} - @vdc.ACLS = acls_str + rc, acl_ids = create_acls(rules) + return rollback(group, user, acl_ids,rc) if OpenNebula.is_error?(rc) + + OzonesServer::logger.debug {"ACLs #{acl_ids} created"} + + rsrc[:ACLS] = acl_ids + @vdc.resources = rsrc 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 + #------------------------------------------------------------------- + # Delete users from a group and the group + #------------------------------------------------------------------- up = OpenNebula::UserPool.new(@client) up.info @@ -166,7 +223,6 @@ module OZones end } - # Delete the group OpenNebula::Group.new_with_id(@vdc.GROUP_ID, @client).delete return @vdc.destroy @@ -180,33 +236,37 @@ module OZones OpenNebula::Group.new_with_id(@vdc.GROUP_ID, @client).delete end - def update(host_list) + def update(rsrc_hash) + # ------------------------------------------------------------------ # Delete existing host ACLs - delete_host_acls + # ------------------------------------------------------------------ + delete_resource_acls - if @vdc.ACLS =~ /((\d+,){#{HOST_ACL_FIRST_ID}}).*/ - newacls = $1.chop - else - newacls = @vdc.ACLS.clone - end + acls = @vcd.resources[:ACLS] + acls.slice!(RESOURCE_ACL_FIRST_ID..-1) + + # ------------------------------------------------------------------ # Create new ACLs. TODO Rollback ACL creation - if !host_list.empty? - host_acls = get_host_acls(host_list) - rc, acls_str = create_acls(host_acls) + # ------------------------------------------------------------------ + if !rsrc_hash.nil? + rsrc_acls = get_resource_acls(rsrc_hash) + rc, acls_ids = create_acls(rsrc_acls) return rc if OpenNebula.is_error?(rc) - #Create the new acl string. - newacls << "," << acls_str + acls << acls_id + end + rsrc_hash[:ACLS] = acls + # ------------------------------------------------------------------ #Update the VDC Record + # ------------------------------------------------------------------ begin @vdc.raise_on_save_failure = true - @vdc.HOSTS = host_list - @vdc.ACLS = newacls + @vdc.resources = rsrc_hash @vdc.save rescue => e @@ -220,63 +280,67 @@ module OZones ####################################################################### # Functions to generate ACL Strings ####################################################################### - # The ID of the first host ACL - HOST_ACL_FIRST_ID = 3 + # The ID of the first resource ACL + RESOURCE_ACL_FIRST_ID = 3 # This method returns an Array of ACL strings to create them # in the target zone - def get_acls + def get_acls(rsrc_hash) rule_str = Array.new # Grant permissions to the group - rule_str << "@#{@vdc.GROUP_ID} VM+NET+IMAGE+TEMPLATE/* CREATE" + rule_str << "@#{@vdc.GROUP_ID} VM+IMAGE+TEMPLATE/* CREATE" # Grant permissions to the vdc admin rule_str << "##{@vdc.VDCADMIN_ID} USER/* CREATE" rule_str << "##{@vdc.VDCADMIN_ID} USER/@#{@vdc.GROUP_ID} " \ "USE+MANAGE+ADMIN" - ############################################################### - #When more rules are added the class constant HOST_ACL_FIRST_ID + #################################################################### + #When more rules are added the class constant RESOURCE_ACL_FIRST_ID #must be modified - ############################################################### + #################################################################### - rule_str.concat(get_host_acls) + rule_str.concat(get_resource_acls(rsrc_hash)) end - def get_host_acls(host_list = nil) - rule_str = Array.new - - if host_list == nil - host_list = @vdc.HOSTS - end + def get_resource_acls(rsrc_hash) + rule_str = Array.new # Grant permissions to use the vdc hosts - host_list.split(',').each{|hostid| + rsrc_hash[:HOSTS].each{ |hostid| rule_str << "@#{@vdc.GROUP_ID} HOST/##{hostid} MANAGE" } + # Grant permissions to use the vdc datastores + rsrc_hash[:DATASTORES].each{ |dsid| + rule_str << "@#{@vdc.GROUP_ID} DATASTORE/##{dsid} USE" + } + + # Grant permissions to use the vdc networks + rsrc_hash[:NETWORKS].each{ |netid| + rule_str << "@#{@vdc.GROUP_ID} NET/##{netid} USE" + } + return rule_str end ####################################################################### # Functions to delete resources associated to the VDC ####################################################################### - # Deletes ACLs for the hosts - def delete_host_acls - host_acls = @vdc.ACLS.split(',')[HOST_ACL_FIRST_ID..-1] - - if host_acls - host_acls.each{|acl| - OpenNebula::Acl.new_with_id(acl.to_i, @client).delete - } - end + # Deletes ACLs for the resources + def delete_resource_acls + delete_acls(RESOURCE_ACL_FIRST_ID) end # Delete ACLs - def delete_acls - @vdc.ACLS.split(",").each{|acl| - OpenNebula::Acl.new_with_id(acl.to_i, @client).delete + def delete_acls(first_id = 0) + rsrc = @vdc.resources + + return if rsrc[:ACLS].nil? + + rsrc[:ACLS][first_id..-1].each { |acl_id| + OpenNebula::Acl.new_with_id(acl_id, @client).delete } end @@ -310,16 +374,6 @@ module OZones } 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 ####################################################################### @@ -330,9 +384,8 @@ module OZones user.delete if user if acls - acls.chop - acls.split(",").each{|acl| - OpenNebula::Acl.new_with_id(acl.to_i, @client).delete + acls.each{|acl_id| + OpenNebula::Acl.new_with_id(acl_id, @client).delete } end @@ -342,7 +395,7 @@ module OZones # Creates an acl array of acl strings. Returns true or error and # a comma-separated list with the new acl ids def create_acls(acls) - acls_str = "" + acls_ids = Array.new rc = true acls.each{|rule| @@ -351,10 +404,10 @@ module OZones break if OpenNebula.is_error?(rc) - acls_str << acl.id.to_s << "," + acls_ids << acl.id } - return rc, acls_str.chop + return rc, acls_ids end end end diff --git a/src/ozones/Server/models/OzonesServer.rb b/src/ozones/Server/models/OzonesServer.rb index 4b7a952af5..903a3bff4d 100644 --- a/src/ozones/Server/models/OzonesServer.rb +++ b/src/ozones/Server/models/OzonesServer.rb @@ -15,10 +15,8 @@ #--------------------------------------------------------------------------- # require 'CloudServer' - require 'JSONUtils' - class OzonesServer < CloudServer include OpenNebulaJSON::JSONUtils @@ -93,27 +91,17 @@ class OzonesServer < CloudServer end #Get the Zone that will host the VDC. And check resouces - zoneid = vdc_data.delete(:ZONEID) - force = vdc_data.delete(:FORCE) + zoneid = vdc_data.delete(:ZONE_ID) if !zoneid - return [400, - OZones::Error.new("Error: Couldn't create vdc. " \ - "Mandatory attribute zoneid missing.").to_json] + return [400, OZones::Error.new("Error: Couldn't create vdc. " \ + "Mandatory attribute zoneid missing.").to_json] end zone = OZones::Zones.get(zoneid) if !zone return [404, OZones::Error.new("Error: Couldn't create vdc. " \ - "Zone #{zoneid} not found.").to_json] - end - - if (!force or force.upcase!="YES") and - !host_uniqueness?(zone, vdc_data[:HOSTS]) - - return [403, - OZones::Error.new("Error: Couldn't create vdc. " \ - "Hosts are not unique, use force to override").to_json] + "Zone #{zoneid} not found.").to_json] end # Create de VDC @@ -132,11 +120,11 @@ class OzonesServer < CloudServer begin zone.save rescue => e - vdc.clean_bootstrap + #vdc.clean_bootstrap + logger.error {"create_vdc: #{e.resource.errors.inspect}"} - return [400, - OZones::Error.new("Error: Couldn't create " \ - "vdc. Zone could not be saved: #{e.message}").to_json] + 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 @@ -174,13 +162,12 @@ class OzonesServer < CloudServer "Reason: #{data.message}.").to_json] end - hosts = vdc_data.delete(:HOSTS) - force = vdc_data.delete(:FORCE) + rsrc = vdc_data.delete(:RESOURCES) # Check parameters - if !hosts + if !rsrc return [400, OZones::Error.new("Error: Couldn't update vdc. " \ - "Missing HOSTS.").to_json] + "Missing RESOURCES.").to_json] end # Check if the referenced Vdc exists @@ -191,15 +178,7 @@ class OzonesServer < CloudServer "#{e.message}").to_json] end - if (!force or force.upcase != "YES") and - !host_uniqueness?(vdc.zone, hosts, vdc_id.to_i) - - return [403, - OZones::Error.new("Error: Couldn't update vdc. " \ - "Hosts are not unique, use force to override").to_json] - end - - rc = vdc.update(hosts) + rc = vdc.update(rsrc) if !OpenNebula.is_error?(rc) return [200, rc] @@ -249,29 +228,4 @@ class OzonesServer < CloudServer return [200, OZones.str_to_json("Zone #{id} successfully deleted")] end end - - ############################################################################ - # Misc Helper Functions - ############################################################################ - private - - # Check if hosts are already include in any Vdc of the zone - def host_uniqueness?(zone, host_list, vdc_id = -1) - return true if host_list.empty? - - all_hosts = "" - zone.vdcs.all.each{|vdc| - if vdc.HOSTS != nil and !vdc.HOSTS.empty? and vdc.id != vdc_id - all_hosts << ',' << vdc.HOSTS - end - } - - all_hosts = all_hosts.split(',') - - host_list.split(",").each{|host| - return false if all_hosts.include?(host) - } - - return true - end end