From e4577e50231324509062065ca52a17152525b602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Gonz=C3=A1lez?= Date: Thu, 22 Oct 2020 12:55:27 +0200 Subject: [PATCH] F #5112: Increase onegate functionality for VNF (#339) --- include/RequestManagerPoolInfoFilter.h | 12 + install.sh | 3 +- share/onegate/onegate | 130 +++++- src/oca/go/src/goca/vm.go | 14 + .../client/vm/VirtualMachinePool.java | 15 + .../ruby/opennebula/virtual_machine_pool.rb | 39 +- src/onegate/etc/onegate-server.conf | 16 + src/onegate/onegate-server.rb | 385 +++++++++++++++++- src/rm/RequestManager.cc | 2 + src/rm/RequestManagerPoolInfoFilter.cc | 41 ++ 10 files changed, 617 insertions(+), 40 deletions(-) diff --git a/include/RequestManagerPoolInfoFilter.h b/include/RequestManagerPoolInfoFilter.h index 6a1774dd1c8..6b0fd4beb1b 100644 --- a/include/RequestManagerPoolInfoFilter.h +++ b/include/RequestManagerPoolInfoFilter.h @@ -133,6 +133,18 @@ class VirtualMachinePoolInfoExtended : public VirtualMachinePoolInfo /* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */ +class VirtualMachinePoolInfoSet : public RequestManagerPoolInfoFilter +{ +public: + VirtualMachinePoolInfoSet(); + + void request_execute( + xmlrpc_c::paramList const& paramList, RequestAttributes& att) override; +}; + +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + class VirtualMachinePoolAccounting : public RequestManagerPoolInfoFilter { public: diff --git a/install.sh b/install.sh index 8959b9e0d0f..ea56e64ce7d 100755 --- a/install.sh +++ b/install.sh @@ -2649,7 +2649,8 @@ FIREEDGE_ETC_FILES="src/fireedge/fireedge-server.conf" #----------------------------------------------------------------------------- ONEGATE_FILES="src/onegate/onegate-server.rb \ - src/onegate/config.ru" + src/onegate/config.ru \ + share/onegate/onegate" ONEGATE_BIN_FILES="src/onegate/bin/onegate-server" diff --git a/share/onegate/onegate b/share/onegate/onegate index fdd52c6e231..dc4d7d49383 100755 --- a/share/onegate/onegate +++ b/share/onegate/onegate @@ -394,6 +394,35 @@ module OneGate end end + # Virtual Router module + module VirtualRouter + + def self.print(json_hash, _extended = false) + OneGate.print_header('VROUTER ' + json_hash['VROUTER']['ID']) + OneGate.print_key_value('NAME', json_hash['VROUTER']['NAME']) + + vms_ids = Array(json_hash['VROUTER']['VMS']['ID']) + + vms = vms_ids.join(',') + + OneGate.print_key_value('VMS', vms) + puts + end + + end + + # Virtual Network module + module VirtualNetwork + + def self.print(json_hash, _extended = false) + OneGate.print_header('VNET') + OneGate.print_key_value('ID', json_hash['VNET']['ID']) + + puts + end + + end + class Client def initialize(opts={}) @vmid = ENV["VMID"] @@ -473,8 +502,8 @@ module OneGate def self.parse_json(response) if CloudClient::is_error?(response) - puts "ERROR: " - puts response.message + STDERR.puts 'ERROR: ' + STDERR.puts response.message exit -1 else return JSON.parse(response.body) @@ -537,6 +566,10 @@ Available commands $ onegate service show [--json][--extended] $ onegate service scale --role ROLE --cardinality CARDINALITY + + $ onegate vrouter show [--json] + + $ onegate vnet show VNETID [--json][--extended] EOT end end @@ -576,7 +609,7 @@ OptionParser.new do |opts| end opts.on("-h", "--help", "Show this message") do - puts OneGate.help_str + STDERR.puts OneGate.help_str exit end end.parse! @@ -601,7 +634,7 @@ when "vm" end when "update" if !options[:data] && !options[:erase] - puts "You have to provide the data as a param (--data, --erase)" + STDERR.puts 'You have to provide the data as a param (--data, --erase)' exit -1 end @@ -618,8 +651,8 @@ when "vm" end if CloudClient::is_error?(response) - puts "ERROR: " - puts response.message + STDERR.puts 'ERROR: ' + STDERR.puts response.message exit -1 end when "resume", @@ -649,18 +682,18 @@ when "vm" response = client.post("/vms/"+ARGV[2]+"/action", action_hash.to_json) if CloudClient::is_error?(response) - puts "ERROR: " - puts response.message + STDERR.puts 'ERROR: ' + STDERR.puts response.message exit -1 end else - puts "You have to provide a VM ID" + STDERR.puts 'You have to provide a VM ID' exit -1 end else - puts OneGate.help_str - puts - puts "Action #{ARGV[1]} not supported" + STDERR.puts OneGate.help_str + STDERR.puts + STDERR.puts "Action #{ARGV[1]} not supported" exit -1 end when "service" @@ -693,18 +726,79 @@ when "service" }.to_json) if CloudClient::is_error?(response) - puts "ERROR: " - puts response.message + STDERR.puts 'ERROR: ' + STDERR.puts response.message exit -1 end else - puts OneGate.help_str - puts - puts "Action #{ARGV[1]} not supported" + STDERR.puts OneGate.help_str + STDERR.puts + STDERR.puts "Action #{ARGV[1]} not supported" exit -1 end +when 'vrouter' + case ARGV[1] + when 'show' + if options[:extended] + extra = {} + extra['extended'] = true + + extra = URI.encode_www_form(extra) + end + + response = client.get('/vrouter', extra) + json_hash = OneGate.parse_json(response) + + if options[:json] + puts JSON.pretty_generate(json_hash) + else + if options[:extended] + OneGate::VirtualRouter.print(json_hash, true) + else + OneGate::VirtualRouter.print(json_hash) + end + end + else + STDERR.puts OneGate.help_str + STDERR.puts + STDERR.puts "Action #{ARGV[1]} not supported" + exit(-1) + end +when 'vnet' + case ARGV[1] + when 'show' + if ARGV[2] + if options[:extended] + extra = {} + extra['extended'] = true + + extra = URI.encode_www_form(extra) + end + + response = client.get('/vnet/'+ARGV[2], extra) + json_hash = OneGate.parse_json(response) + + if options[:json] + puts JSON.pretty_generate(json_hash) + else + if options[:extended] + OneGate::VirtualNetwork.print(json_hash, true) + else + OneGate::VirtualNetwork.print(json_hash) + end + end + else + STDERR.puts 'You have to provide a VNET ID' + exit -1 + end + else + STDERR.puts OneGate.help_str + STDERR.puts + STDERR.puts "Action #{ARGV[1]} not supported" + exit(-1) + end else - puts OneGate.help_str + STDERR.puts OneGate.help_str exit -1 end diff --git a/src/oca/go/src/goca/vm.go b/src/oca/go/src/goca/vm.go index 2b153cb82aa..f4aff78eeca 100644 --- a/src/oca/go/src/goca/vm.go +++ b/src/oca/go/src/goca/vm.go @@ -159,6 +159,20 @@ func (vc *VMsController) InfoExtendedFilter(f *VMFilter) (*vm.Pool, error) { return vmPool, nil } +// InfoSet connects to OpenNebula and fetches a VM_POOL containing the VMs in vmIds +func (vc *VMsController) InfoSet(vmIds string, extended bool) (*vm.Pool, error) { + response, err := vc.c.Client.Call("one.vmpool.infoset", vmIds, extended) + if err != nil { + return nil, err + } + vmPool := &vm.Pool{} + err = xml.Unmarshal([]byte(response.Body()), vmPool) + if err != nil { + return nil, err + } + return vmPool, nil +} + // Info connects to OpenNebula and fetches the information of the VM func (vc *VMController) Info(decrypt bool) (*vm.VM, error) { response, err := vc.c.Client.Call("one.vm.info", vc.ID, decrypt) diff --git a/src/oca/java/src/org/opennebula/client/vm/VirtualMachinePool.java b/src/oca/java/src/org/opennebula/client/vm/VirtualMachinePool.java index f0b24bbab7f..3850661130f 100644 --- a/src/oca/java/src/org/opennebula/client/vm/VirtualMachinePool.java +++ b/src/oca/java/src/org/opennebula/client/vm/VirtualMachinePool.java @@ -34,6 +34,7 @@ public class VirtualMachinePool extends Pool implements Iterable private static final String ELEMENT_NAME = "VM"; private static final String INFO_METHOD = "vmpool.info"; private static final String INFO_EXTENDED_METHOD = "vmpool.infoextended"; + private static final String INFO_SET_METHOD = "vmpool.infoset"; private static final String MONITORING = "vmpool.monitoring"; /** @@ -133,6 +134,20 @@ public static OneResponse info_extended(Client client, int filter) return client.call(INFO_EXTENDED_METHOD, filter, -1, -1, NOT_DONE); } + /** + * Retrieves all of the Virtual Machines in the vm_ids list. + * + * @param client XML-RPC Client. + * @param vm_ids Comma separated list of VM IDs. + * @param extended If true the extended body is retrieved. + * @return If successful the message contains the string + * with the information returned by OpenNebula. + */ + public static OneResponse info_extended(Client client, int vm_ids, boolean extended) + { + return client.call(INFO_SET_METHOD, vm_ids, extended); + } + /** * Retrieves all the Virtual Machines in the pool. * diff --git a/src/oca/ruby/opennebula/virtual_machine_pool.rb b/src/oca/ruby/opennebula/virtual_machine_pool.rb index 8c7c6b0acc0..cf52744dc53 100644 --- a/src/oca/ruby/opennebula/virtual_machine_pool.rb +++ b/src/oca/ruby/opennebula/virtual_machine_pool.rb @@ -23,14 +23,14 @@ class VirtualMachinePool < Pool # Constants and Class attribute accessors ####################################################################### - VM_POOL_METHODS = { - :info => "vmpool.info", - :info_extended => "vmpool.infoextended", - :monitoring => "vmpool.monitoring", - :accounting => "vmpool.accounting", - :showback => "vmpool.showback", - :calculate_showback => "vmpool.calculateshowback" + :info => 'vmpool.info', + :info_extended => 'vmpool.infoextended', + :info_set => 'vmpool.infoset', + :monitoring => 'vmpool.monitoring', + :accounting => 'vmpool.accounting', + :showback => 'vmpool.showback', + :calculate_showback => 'vmpool.calculateshowback' } # Constants for info queries (include/RequestManagerPoolInfoFilter.h) @@ -41,7 +41,6 @@ class VirtualMachinePool < Pool # Class constructor & Pool Methods ####################################################################### - # +client+ a Client object that represents a XML-RPC connection # +user_id+ is to refer to a Pool with VirtualMachines from that user def initialize(client, user_id=0) @@ -102,21 +101,21 @@ def info(*args) end # Define info methods shortcuts for different filters - # info_all() + # info_all() # info_all!() - # info_all_extended + # info_all_extended # info_all_extended!() - # info_mine() + # info_mine() # info_mine!() - # info_mine_extended + # info_mine_extended # info_mine_extended!() - # info_group() + # info_group() # info_group!() - # info_group_extended + # info_group_extended # info_group_extended!() - # info_primary_group() + # info_primary_group() # info_primary_group!() - # info_primary_group_extended + # info_primary_group_extended # info_primary_group_extended!() %w[mine all group primary_group].each do |ifilter| const_name = "OpenNebula::Pool::INFO_#{ifilter.upcase}" @@ -160,6 +159,14 @@ def info_search(args = {}) default_args[:query]) end + # Retrieves the set of VMs especified in vm_ids + # + # @param [String] comma separated list of vm ids. + # @param [Boolean] if true extended body is retrieved. + # + def info_set(vm_ids, extended) + xmlrpc_info(VM_POOL_METHODS[:info_set], vm_ids, extended) + end # Retrieves the monitoring data for all the VMs in the pool # diff --git a/src/onegate/etc/onegate-server.conf b/src/onegate/etc/onegate-server.conf index 55832849370..be6fc09f191 100644 --- a/src/onegate/etc/onegate-server.conf +++ b/src/onegate/etc/onegate-server.conf @@ -76,6 +76,10 @@ :service: :show: true :change_cardinality: true + :vrouter: + :show: true + :vnet: + :show_by_id: true # Attrs that cannot be modified when updating a VM template :restricted_attrs: @@ -83,6 +87,18 @@ - SERVICE_ID - ROLE_NAME +# Attrs of the Virtual Network template that will be retrieved +# with onegate vnet | get /vnet/:id requests. +:vnet_template_attributes: + - NETWORK_ADDRESS + - NETWORK_MASK + - GATEWAY + - GATEWAY6 + - DNS + - GUEST_MTU + - CONTEXT_FORCE_IPV4 + - SEARCH_DOMAIN + # Actions that cannot be performed on a VM :restricted_actions: #- deploy diff --git a/src/onegate/onegate-server.rb b/src/onegate/onegate-server.rb index a167f5bddae..e2344756175 100644 --- a/src/onegate/onegate-server.rb +++ b/src/onegate/onegate-server.rb @@ -50,6 +50,7 @@ require 'sinatra' require 'yaml' require 'json' +require 'set' require 'CloudAuth' require 'CloudServer' @@ -75,6 +76,11 @@ 'reboot' ] +# Attrs of the Virtual Network template that will be retrieved +# with onegate vnet | get /vnet/:id requests. +VNET_TEMPLATE_ATTRIBUTES = %w[NETWORK_ADDRESS NETWORK_MASK GATEWAY GATEWAY6 DNS + GUEST_MTU CONTEXT_FORCE_IPV4 SEARCH_DOMAIN] + include OpenNebula begin @@ -162,13 +168,24 @@ def get_source_vm(request_env, client) def get_requested_vm(requested_vm_id, request_env, client) source_vm = get_source_vm(request_env, client) - if source_vm['ID'].to_i != requested_vm_id + + return source_vm if Integer(source_vm['ID']) == requested_vm_id + + if !source_vm['USER_TEMPLATE/SERVICE_ID'].nil? service_id = source_vm['USER_TEMPLATE/SERVICE_ID'] check_vm_in_service(requested_vm_id, service_id, client) - - requested_vm = get_vm(requested_vm_id, client) + get_vm(requested_vm_id, client) + elsif !source_vm['TEMPLATE/VROUTER_ID'].nil? + vrouter_id = source_vm['TEMPLATE/VROUTER_ID'] + vrouter_hash = get_vrouter(vrouter_id, client).to_hash + check_vm_in_vrouter(requested_vm_id, vrouter_hash, source_vm) + get_vm(requested_vm_id, client) else - requested_vm = source_vm + error_msg = 'This VM does not belong to any Virtual Router or '\ + 'Service, so it cannot retrieve information '\ + 'from other VMs' + logger.error {error_msg} + halt 400, error_msg end end @@ -241,6 +258,48 @@ def get_service(service_id, client) service.body end + def get_vrouter(vrouter_id, client) + begin + vrouter_id = Integer(vrouter_id) + rescue TypeError + error_msg = 'Empty or invalid VROUTER_ID' + logger.error {error_msg} + halt 400, error_msg + end + + vrouter = VirtualRouter.new_with_id(vrouter_id, client) + rc = vrouter.info + + if OpenNebula.is_error?(rc) + error_msg = "Virtual router #{vrouter_id} not found" + logger.error {error_msg} + halt 404, error_msg + end + + vrouter + end + + def get_vnet(vnet_id, client) + begin + vnet_id = Integer(vnet_id) + rescue TypeError + error_msg = 'Empty or invalid VNET_ID' + logger.error {error_msg} + halt 400, error_msg + end + + vnet = VirtualNetwork.new_with_id(vnet_id, client) + rc = vnet.info + + if OpenNebula.is_error?(rc) + error_msg = "Virtual network #{vnet_id} not found" + logger.error {error_msg} + halt 404, error_msg + end + + vnet + end + def parse_json(json_str, root_element) begin hash = JSON.parse(json_str) @@ -294,6 +353,10 @@ def check_permissions(resource, action) end end + def get_vnet_template_attributes + $conf[:vnet_template_attributes] || VNET_TEMPLATE_ATTRIBUTES + end + # Check if the source VM is part of a service and if the requested # VM is part of the same Service as the source VM. # @@ -326,6 +389,91 @@ def check_vm_in_service(requested_vm_id, service_id, client, extended = false) return response end + # Check if the source VM is part of a virtual router and if the + # requested VM is part of the same virtual router as the source VM. + # If false a halt is triggered + # + # @param requested_vm_id [Integer] + # @param vrouter_hash [Hash] + # @param source_vm [OpenNebula::VirtualMachine] + # + def check_vm_in_vrouter(requested_vm_id, vrouter_hash, source_vm) + # Check that the user has not spoofed the VROUTER_ID + vrouter_vm_ids = Array(vrouter_hash['VROUTER']['VMS']['ID']).map! do |vm| + Integer(vm) + end + + if !vrouter_vm_ids.include?(requested_vm_id) || + !vrouter_vm_ids.include?(source_vm.id) + + error_msg = "Virtual Router #{vrouter_hash['VROUTER']['ID']} does "\ + "not contain VM #{requested_vm_id}." + logger.error {error_msg} + halt 400, error_msg + end + end + + # Check if the requested VNET can be accessed from the curren VROUTER. + # If false a halt is triggered. + # + # @param req_vnet [OpenNebula::VirtualNetwork] + # @param vrouter_hash [Hash] + # @param client [OpenNebula::Client] + # + def check_vnet_in_vrouter(req_vnet, vrouter_hash, client) + valid_vnets = Set[] + + # Get VR nics + nics = vrouter_hash['VROUTER']['TEMPLATE']['NIC'] + + if !nics.is_a?(Array) + nics = [nics] + end + + # Get only one nic if multiple nic in same network + nics.uniq! {|n| n['NETWORK_ID'] } + + have_access = false + nics.each do |nic| + # Get nic VNET + nic_vnet = get_vnet(nic['NETWORK_ID'], client) + + # Provide access to nic's VNET + valid_vnets.add(Integer(nic['NETWORK_ID'])) + # Provide access to nic's VNET parent (if exists) + if !nic_vnet['PARENT_NETWORK_ID'].nil? && + !nic_vnet['PARENT_NETWORK_ID'].empty? + valid_vnets.add(Integer(nic_vnet['PARENT_NETWORK_ID'])) + end + # Provide access to nic's VNET childs + xpath = '//LEASE/VNET' + childs = nic_vnet.retrieve_xmlelements(xpath) + + childs.each do |c| + valid_vnets.add(Integer(c.text)) + end + # Provide access to VNETs with same parent as NIC network + if !valid_vnets.include?(req_vnet.id) + # Get requested vnet parent + if !req_vnet['PARENT_NETWORK_ID'].empty? + req_parent = Integer(req_vnet['PARENT_NETWORK_ID']) + end + + next unless valid_vnets.include?(req_parent) + end + + have_access = true + break + end + + return if have_access + + error_msg = "Virtual Network #{req_vnet.id} cannot be retrieved"\ + " from Virtual router #{vrouter_hash['VROUTER']['ID']}." + logger.error {error_msg} + halt 400, error_msg + end + # Escape data from user def scape_attr(attr) ret = '' @@ -420,7 +568,7 @@ def update(object, params) end end -NIC_VALID_KEYS = %w(IP IP6_LINK IP6_SITE IP6_GLOBAL NETWORK MAC NAME PARENT) +NIC_VALID_KEYS = %w(IP IP6_LINK IP6_SITE IP6_GLOBAL NETWORK MAC NAME PARENT EXTERNAL) USER_TEMPLATE_INVALID_KEYS = %w(SCHED_MESSAGE) def build_vm_hash(vm_hash) @@ -514,6 +662,161 @@ def build_service_hash(service_hash, client = nil, extended = false) } end +def build_vrouter_hash(vrouter_hash, _client = nil, _extended = false) + vrouter = { + 'VROUTER' => { + 'NAME' => vrouter_hash['VROUTER']['NAME'], + 'ID' => vrouter_hash['VROUTER']['ID'], + 'VMS' => vrouter_hash['VROUTER']['VMS'], + 'TEMPLATE' => vrouter_hash['VROUTER']['TEMPLATE'] + } + } + + # Manage special cases (arrays) + if !vrouter['VROUTER']['TEMPLATE']['NIC'].is_a?(Array) + if vrouter['VROUTER']['TEMPLATE']['NIC'].nil? + vrouter['VROUTER']['TEMPLATE']['NIC'] = [] + else + vrouter['VROUTER']['TEMPLATE']['NIC'] = [ + vrouter['VROUTER']['TEMPLATE']['NIC'] + ] + end + end + + if !vrouter_hash['VROUTER']['VMS']['ID'].is_a?(Array) + if vrouter_hash['VROUTER']['VMS']['ID'].nil? + vrouter_hash['VROUTER']['VMS']['ID'] = [] + else + vrouter_hash['VROUTER']['VMS']['ID'] = [ + vrouter_hash['VROUTER']['VMS']['ID'] + ] + end + end + + vrouter +end + +VNET_ATTRIBUTES = %w[ID NAME USED_LEASES VROUTERS PARENT_NETWORK_ID AR_POOL] + +def process_vnet(vnet_hash) + template = {} + + get_vnet_template_attributes.each do |key| + value = vnet_hash['VNET']['TEMPLATE'][key] + template[key] = value unless value.nil? + end + + vnet = {} + VNET_ATTRIBUTES.each do |key| + vnet[key] = vnet_hash['VNET'][key] + end + + vnet['TEMPLATE'] = template + + # Manage special cases (arrays) + if !vnet['AR_POOL']['AR'].is_a?(Array) + if vnet['AR_POOL']['AR'].nil? + vnet['AR_POOL']['AR'] = [] + else + vnet['AR_POOL']['AR'] = [vnet['AR_POOL']['AR']] + end + end + + vnet['AR_POOL']['AR'].each do |ar| + if !ar['LEASES']['LEASE'].is_a?(Array) + if ar['LEASES']['LEASE'].nil? + ar['LEASES']['LEASE'] = [] + else + ar['LEASES']['LEASE'] = [ar['LEASES']['LEASE']] + end + end + end + + if !vnet['VROUTERS']['ID'].is_a?(Array) + if vnet['VROUTERS']['ID'].nil? + !vnet['VROUTERS']['ID'] = [] + else + vnet['VROUTERS']['ID'] = [vnet['VROUTERS']['ID']] + end + end + + vnet +end + +def build_vnet_hash(vnet, client, extended) + # if extended flag is not set + if extended.nil? || extended.downcase != 'true' + vnet = vnet.to_hash + vnet['VNET'] = process_vnet(vnet) + + return vnet + end + + vm_pool = VirtualMachinePool.new(client) + + # get VMs that are using the VNET + vms = '' + vnet.retrieve_xmlelements('//LEASE/VM').each do |vm| + vms << ',' unless vms.empty? + vms << vm.text + end + + vnet = vnet.to_hash + vnet['VNET'] = process_vnet(vnet) + + rc = vm_pool.info_set(vms, true) + if OpenNebula.is_error?(rc) + logger.error {"vmpool.info error: #{rc.message}"} + halt 404, rc.message + end + + # Get ARs array + ars = vnet['VNET']['AR_POOL']['AR'] + # rubocop:disable Style/ArrayCoercion + ars = [ars] unless ars.is_a?(Array) + + ars.each do |ar| + leases = ar['LEASES']['LEASE'] + + next if leases.nil? + + leases = [leases] unless leases.is_a?(Array) + # rubocop:enable Style/ArrayCoercion + + leases.each do |lease| + next if lease['VM'].nil? + + # Get the corresponding VM from pool + xpath = "/VM_POOL/VM[ID=#{lease['VM']}]" + vm = vm_pool.retrieve_xmlelements(xpath)[0] + + # Get corresponding NIC from VM (MAC should be unique) + xpath = "./TEMPLATE/NIC[MAC=\"#{lease['MAC']}\"]" + nic = vm.retrieve_xmlelements(xpath)[0] + + if nic.nil? + xpath = "./TEMPLATE/NIC_ALIAS[MAC=\"#{lease['MAC']}\"]" + nic = vm.retrieve_xmlelements(xpath)[0] + + # get parent network + xpath = "./TEMPLATE/NIC[NIC_ID=\"#{nic['PARENT_ID']}\"]/NETWORK_ID" + parent_id = vm.retrieve_xmlelements(xpath)[0].text + + # Get ALIAS extended info + lease['PARENT'] = nic['PARENT'] + lease['PARENT_NETWORK_ID'] = parent_id + lease['EXTERNAL'] = !nic['EXTERNAL'].nil? && + nic['EXTERNAL'].downcase == 'yes' + end + + # Get extended info + lease['NIC_NAME'] = nic['NAME'] + end + end + + vnet +end + get '/' do client = authenticate(request.env, params) @@ -565,6 +868,70 @@ def build_service_hash(service_hash, client = nil, extended = false) [200, response.to_json] end +get '/vrouter' do + check_permissions(:vrouter, :show) + client = authenticate(request.env, params) + + source_vm = get_source_vm(request.env, client) + vrouter_id = source_vm['TEMPLATE/VROUTER_ID'] + vrouter_hash = get_vrouter(vrouter_id, client).to_hash + + check_vm_in_vrouter(Integer(source_vm['ID']), vrouter_hash, source_vm) + + response = build_vrouter_hash(vrouter_hash, client, params['extended']) rescue nil + if response.nil? + error_msg = "Virtual router #{vrouter_id} is empty." + logger.error {error_msg} + halt 400, error_msg + end + + [200, response.to_json] +end + +get '/vnet/:id' do + check_permissions(:vnet, :show_by_id) + client = authenticate(request.env, params) + + # Check :id is an integer + vnet_id = begin + Integer(params[:id]) + rescue ArgumentError + error_msg = "Invalid id format (ID: #{params[:id]}). "\ + 'ID must be an integer.' + logger.error { error_msg } + halt 400, error_msg + end + + source_vm = get_source_vm(request.env, client) + vrouter_id = source_vm['TEMPLATE/VROUTER_ID'] + + # Check if current VM is a VROUTER + if vrouter_id.nil? || vrouter_id.empty? + error_msg = 'Virtual networks information can only be' \ + ' retrieved from Virtual Routers.' + logger.error {error_msg} + halt 400, error_msg + end + + # Retrieve VROUTER information + vrouter_hash = get_vrouter(vrouter_id, client).to_hash + check_vm_in_vrouter(Integer(source_vm['ID']), vrouter_hash, source_vm) + + # Retrieve requested VNET + req_vnet = get_vnet(Integer(vnet_id), client) + check_vnet_in_vrouter(req_vnet, vrouter_hash, client) + + response = build_vnet_hash(req_vnet, client, params['extended']) rescue nil + + if response.nil? + error_msg = "Virtual router #{vrouter_hash['VROUTER']['ID']} is empty." + logger.error {error_msg} + halt 400, error_msg + end + + [200, response.to_json] +end + get '/vms/:id' do check_permissions(:vm, :show_by_id) client = authenticate(request.env, params) @@ -617,3 +984,11 @@ def build_service_hash(service_hash, client = nil, extended = false) [200, ""] end + +%w[get head post put delete options patch].each do |method| + send method, '/*' do + error_msg = 'OneGate server doesn\'t support this feature' + logger.error {error_msg} + halt 400, error_msg + end +end diff --git a/src/rm/RequestManager.cc b/src/rm/RequestManager.cc index dba11328e20..495deb79ab3 100644 --- a/src/rm/RequestManager.cc +++ b/src/rm/RequestManager.cc @@ -444,6 +444,7 @@ void RequestManager::register_xml_methods() xmlrpc_c::methodPtr datastorepool_info(new DatastorePoolInfo()); xmlrpc_c::methodPtr vm_pool_info(new VirtualMachinePoolInfo()); xmlrpc_c::methodPtr vm_pool_info_extended(new VirtualMachinePoolInfoExtended()); + xmlrpc_c::methodPtr vm_pool_info_set(new VirtualMachinePoolInfoSet()); xmlrpc_c::methodPtr template_pool_info(new TemplatePoolInfo()); xmlrpc_c::methodPtr vnpool_info(new VirtualNetworkPoolInfo()); xmlrpc_c::methodPtr vntemplate_pool_info(new VirtualNetworkTemplatePoolInfo()); @@ -571,6 +572,7 @@ void RequestManager::register_xml_methods() RequestManagerRegistry.addMethod("one.vmpool.info", vm_pool_info); RequestManagerRegistry.addMethod("one.vmpool.infoextended", vm_pool_info_extended); + RequestManagerRegistry.addMethod("one.vmpool.infoset", vm_pool_info_set); RequestManagerRegistry.addMethod("one.vmpool.accounting", vm_pool_acct); RequestManagerRegistry.addMethod("one.vmpool.monitoring", vm_pool_monitoring); RequestManagerRegistry.addMethod("one.vmpool.showback", vm_pool_showback); diff --git a/src/rm/RequestManagerPoolInfoFilter.cc b/src/rm/RequestManagerPoolInfoFilter.cc index 736fbc364b5..de8c4236f2e 100644 --- a/src/rm/RequestManagerPoolInfoFilter.cc +++ b/src/rm/RequestManagerPoolInfoFilter.cc @@ -368,6 +368,47 @@ void VirtualMachinePoolInfo::request_execute( /* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */ +VirtualMachinePoolInfoSet::VirtualMachinePoolInfoSet() + : RequestManagerPoolInfoFilter("one.vmpool.infoset", + "Returns a virtual machine instances set", + "A:ss") +{ + Nebula& nd = Nebula::instance(); + pool = nd.get_vmpool(); + auth_object = PoolObjectSQL::VM; +} + +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +void VirtualMachinePoolInfoSet::request_execute( + xmlrpc_c::paramList const& paramList, + RequestAttributes& att) +{ + std::string ids_str = xmlrpc_c::value_string(paramList.getString(1)); + extended = xmlrpc_c::value_boolean(paramList.getBoolean(2)); + + ostringstream and_filter; + ostringstream oss; + std::set ids; + + one_util::split_unique(ids_str, ',', ids); + + if (ids.empty()) + { + std::string empty_pool = ""; + success_response(empty_pool, att); + return; + } + + and_filter << "oid in (" << one_util::join(ids, ',') << ")"; + + dump(att, -2, -1, -1, and_filter.str(), ""); +} + +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + VirtualMachinePoolAccounting::VirtualMachinePoolAccounting() : RequestManagerPoolInfoFilter("one.vmpool.accounting", "Returns the virtual machine history records",