1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-21 14:50:08 +03:00

feature #1112: VDCs are now defined by a set of Hosts, Datastores and Networks from a Cluster of a given Zone. Users are not granted to create new networks. Hosts does not need to be unique

This commit is contained in:
Ruben S. Montero 2012-03-16 05:23:51 +01:00
parent 2eec562361
commit 2fe4daa875
6 changed files with 194 additions and 167 deletions

View File

@ -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
#

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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