lib/vagrant-vcloud/driver/base.rb in vagrant-vcloud-0.1.2 vs lib/vagrant-vcloud/driver/base.rb in vagrant-vcloud-0.2.0

- old
+ new

@@ -13,17 +13,17 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # -require "log4r" -require "vagrant/util/busy" -require "vagrant/util/platform" -require "vagrant/util/retryable" -require "vagrant/util/subprocess" +require 'log4r' +require 'vagrant/util/busy' +require 'vagrant/util/platform' +require 'vagrant/util/retryable' +require 'vagrant/util/subprocess' +require 'awesome_print' - module VagrantPlugins module VCloud module Driver class UnauthorizedAccess < StandardError; end class WrongAPIVersion < StandardError; end @@ -35,11 +35,11 @@ # Main class to access vCloud rest APIs class Base include Vagrant::Util::Retryable def initialize - @logger = Log4r::Logger.new("vagrant::provider::vcloud::base") + @logger = Log4r::Logger.new('vagrant::provider::vcloud::base') end ## # Authenticate against the specified server def login @@ -59,11 +59,10 @@ # friendly helper method to fetch an Organization Id by name # - name (this isn't case sensitive) def get_organization_id_by_name(name) end - ## # friendly helper method to fetch an Organization by name # - name (this isn't case sensitive) def get_organization_by_name(name) end @@ -71,67 +70,67 @@ ## # Fetch details about an organization: # - catalogs # - vdcs # - networks - def get_organization(orgId) + def get_organization(org_id) end ## # Fetch details about a given catalog - def get_catalog(catalogId) + def get_catalog(catalog_id) end ## # Friendly helper method to fetch an catalog id by name # - organization hash (from get_organization/get_organization_by_name) # - catalog name - def get_catalog_id_by_name(organization, catalogName) + def get_catalog_id_by_name(organization, catalog_name) end ## # Friendly helper method to fetch an catalog by name # - organization hash (from get_organization/get_organization_by_name) # - catalog name - def get_catalog_by_name(organization, catalogName) + def get_catalog_by_name(organization, catalog_name) end ## # Fetch details about a given vdc: # - description # - vapps # - networks - def get_vdc(vdcId) + def get_vdc(vdc_id) end ## # Friendly helper method to fetch a Organization VDC Id by name # - Organization object # - Organization VDC Name - def get_vdc_id_by_name(organization, vdcName) + def get_vdc_id_by_name(organization, vdc_name) end ## # Friendly helper method to fetch a Organization VDC by name # - Organization object # - Organization VDC Name - def get_vdc_by_name(organization, vdcName) + def get_vdc_by_name(organization, vdc_name) end ## # Fetch details about a given catalog item: # - description # - vApp templates - def get_catalog_item(catalogItemId) + def get_catalog_item(catalog_item_id) end ## # friendly helper method to fetch an catalogItem by name # - catalogId (use get_catalog_name(org, name)) - # - catalagItemName - def get_catalog_item_by_name(catalogId, catalogItemName) - end + # - catalagItemName + def get_catalog_item_by_name(catalog_id, catalog_item_name) + end ## # Fetch details about a given vapp: # - name # - description @@ -139,162 +138,164 @@ # - IP # - Children VMs: # -- IP addresses # -- status # -- ID - def get_vapp(vAppId) + def get_vapp(vapp_id) end ## # Delete a given vapp # NOTE: It doesn't verify that the vapp is shutdown - def delete_vapp(vAppId) + def delete_vapp(vapp_id) end ## # Suspend a given vapp - def suspend_vapp(vAppId) + def suspend_vapp(vapp_id) end ## # reboot a given vapp # This will basically initial a guest OS reboot, and will only work if # VMware-tools are installed on the underlying VMs. # vShield Edge devices are not affected - def reboot_vapp(vAppId) + def reboot_vapp(vapp_id) end ## # reset a given vapp # This will basically reset the VMs within the vApp # vShield Edge devices are not affected. - def reset_vapp(vAppId) + def reset_vapp(vapp_id) end ## # Boot a given vapp - def poweron_vapp(vAppId) + def poweron_vapp(vapp_id) end ## # Create a vapp starting from a template # # Params: # - vdc: the associated VDC # - vapp_name: name of the target vapp # - vapp_description: description of the target vapp # - vapp_templateid: ID of the vapp template - def create_vapp_from_template(vdc, vapp_name, vapp_description, vapp_templateid, poweron=false) + def create_vapp_from_template(vdc, vapp_name, vapp_description, + vapp_templateid, poweron = false) end ## # Compose a vapp using existing virtual machines # # Params: # - vdc: the associated VDC # - vapp_name: name of the target vapp # - vapp_description: description of the target vapp - # - vm_list: hash with IDs of the VMs to be used in the composing process + # - vm_list: hash with IDs of the VMs used in the composing process # - network_config: hash of the network configuration for the vapp - def compose_vapp_from_vm(vdc, vapp_name, vapp_description, vm_list={}, network_config={}) + def compose_vapp_from_vm(vdc, vapp_name, vapp_description, + vm_list = {}, network_config = {}) end # Fetch details about a given vapp template: # - name # - description # - Children VMs: # -- ID - def get_vapp_template(vAppId) + def get_vapp_template(vapp_id) end ## # Set vApp port forwarding rules # # - vappid: id of the vapp to be modified # - network_name: name of the vapp network to be modified - # - config: hash with network configuration specifications, must contain an array inside :nat_rules with the nat rules to be applied. - def set_vapp_port_forwarding_rules(vappid, network_name, config={}) + # - config: hash with network configuration specifications, must contain + # an array inside :nat_rules with the nat rules to be applied. + def set_vapp_port_forwarding_rules(vapp_id, network_name, config = {}) end ## # Get vApp port forwarding rules # # - vappid: id of the vApp - def get_vapp_port_forwarding_rules(vAppId) + def get_vapp_port_forwarding_rules(vapp_id) end ## # get vApp edge public IP from the vApp ID # Only works when: # - vApp needs to be poweredOn # - FenceMode is set to "natRouted" # - NatType" is set to "portForwarding - # This will be required to know how to connect to VMs behind the Edge device. - def get_vapp_edge_public_ip(vAppId) + # This will be required to know how to connect to VMs behind the Edge. + def get_vapp_edge_public_ip(vapp_id) end ## # Upload an OVF package # - vdcId # - vappName # - vappDescription # - ovfFile # - catalogId # - uploadOptions {} - def upload_ovf(vdcId, vappName, vappDescription, ovfFile, catalogId, uploadOptions={}) + def upload_ovf(vdc_id, vapp_name, vapp_description, ovf_file, + catalog_id, upload_options = {}) end ## # Fetch information for a given task - def get_task(taskid) + def get_task(task_id) end ## # Poll a given task until completion - def wait_task_completion(taskid) + def wait_task_completion(task_id) end ## # Set vApp Network Config - def set_vapp_network_config(vappid, network_name, config={}) + def set_vapp_network_config(vapp_id, network_name, config = {}) end ## # Set VM Network Config - def set_vm_network_config(vmid, network_name, config={}) + def set_vm_network_config(vm_id, network_name, config = {}) end - ## # Set VM Guest Customization Config - def set_vm_guest_customization(vmid, computer_name, config={}) + def set_vm_guest_customization(vm_id, computer_name, config = {}) end ## # Fetch details about a given VM - def get_vm(vmId) + def get_vm(vm_Id) end private ## - # Sends a synchronous request to the vCloud API and returns the response as parsed XML + headers using HTTPClient. - def send_request(params, payload=nil, content_type=nil) - + # Sends a synchronous request to the vCloud API and returns the + # response as parsed XML + headers using HTTPClient. + def send_request(params, payload = nil, content_type = nil) # Create a new HTTP client clnt = HTTPClient.new # Disable SSL cert verification - clnt.ssl_config.verify_mode=(OpenSSL::SSL::VERIFY_NONE) + clnt.ssl_config.verify_mode = (OpenSSL::SSL::VERIFY_NONE) # Suppress SSL depth message - clnt.ssl_config.verify_callback=proc{ |ok, ctx|; true }; - + clnt.ssl_config.verify_callback = proc { |ok, ctx|; true } + extheader = {} + extheader['accept'] = "application/*+xml;version=#{@api_version}" - extheader["accept"] = "application/*+xml;version=#{@api_version}" - if !content_type.nil? extheader['Content-Type'] = content_type end if @auth_key @@ -303,138 +304,186 @@ clnt.set_auth(nil, "#{@username}@#{@org_name}", @password) end url = "#{@api_url}#{params['command']}" + # Massive debug when LOG=DEBUG + # Using awesome_print to get nice XML output for better readability + if @logger.level == 1 + ap "SEND #{url}" + if payload + payload_xml = Nokogiri.XML(payload) + ap payload_xml + end + end + begin - response = clnt.request(params['method'], url, nil, payload, extheader) + response = clnt.request( + params['method'], + url, + nil, + payload, + extheader + ) + if !response.ok? - raise "Warning: unattended code #{response.status} #{response.reason}" + raise "Warning: unattended code #{response.status}" + + " #{response.reason}" end - [Nokogiri.parse(response.body), response.headers] + nicexml = Nokogiri.XML(response.body) + # Massive debug when LOG=DEBUG + # Using awesome_print to get nice XML output for readability + if @logger.level == 1 + ap "RECV #{response.status}" + # Just avoid the task spam. + if !url.index('/task/') + ap nicexml + end + end + + [Nokogiri.parse(response.body), response.headers] rescue SocketError - raise "Impossible to connect, verify endpoint" + raise 'Impossible to connect, verify endpoint' rescue Errno::EADDRNOTAVAIL - raise "Impossible to connect, verify endpoint" + raise 'Impossible to connect, verify endpoint' end - - end - ## - # Upload a large file in configurable chunks, output an optional progressbar - def upload_file(uploadURL, uploadFile, vAppTemplate, config={}) + # Upload a large file in configurable chunks, output an optional + # progressbar + def upload_file(upload_url, upload_file, vapp_template, config = {}) + # Set chunksize to 1M if not specified otherwise + chunk_size = (config[:chunksize] || 1_048_576) + @logger.debug("Set chunksize to #{chunk_size} bytes") - # Set chunksize to 10M if not specified otherwise - chunkSize = (config[:chunksize] || 10485760) + # Set progressbar to default format if not specified otherwise + progressbar_format = ( + config[:progressbar_format] || '%t Progress: %p%% %e' + ) - # Set progress bar to default format if not specified otherwise - progressBarFormat = (config[:progressbar_format] || "%t Progress: %p%% %e") - - # Set progress bar length to 120 if not specified otherwise - progressBarLength = (config[:progressbar_length] || 80) - # Open our file for upload - uploadFileHandle = File.new(uploadFile, "rb" ) - fileName = File.basename(uploadFileHandle) + upload_file_handle = File.new(upload_file, 'rb') + file_name = File.basename(upload_file_handle) - progressBarTitle = "Uploading: " + fileName.to_s + # FIXME: I removed the filename below because I recall a weird issue + # of upload failing because if a too long filename + # (tsugliani) + progressbar_title = 'Uploading Box...' # Create a progressbar object if progress bar is enabled - if config[:progressbar_enable] == true && uploadFileHandle.size.to_i > chunkSize + if config[:progressbar_enable] == true && + upload_file_handle.size.to_i > chunk_size progressbar = ProgressBar.create( - :title => progressBarTitle, - :starting_at => 0, - :total => uploadFileHandle.size.to_i, - ##:length => progressBarLength, - :format => progressBarFormat + :title => progressbar_title, + :starting_at => 0, + :total => upload_file_handle.size.to_i, + :format => progressbar_format ) else - puts progressBarTitle + puts progressbar_title end # Create a new HTTP client clnt = HTTPClient.new # Disable SSL cert verification - clnt.ssl_config.verify_mode=(OpenSSL::SSL::VERIFY_NONE) + clnt.ssl_config.verify_mode = (OpenSSL::SSL::VERIFY_NONE) # Suppress SSL depth message - clnt.ssl_config.verify_callback=proc{ |ok, ctx|; true }; + clnt.ssl_config.verify_callback = proc { |ok, ctx|; true } # Perform ranged upload until the file reaches its end - until uploadFileHandle.eof? + until upload_file_handle.eof? # Create ranges for this chunk upload - rangeStart = uploadFileHandle.pos - rangeStop = uploadFileHandle.pos.to_i + chunkSize + range_start = upload_file_handle.pos + range_stop = upload_file_handle.pos.to_i + chunk_size # Read current chunk - fileContent = uploadFileHandle.read(chunkSize) + file_content = upload_file_handle.read(chunk_size) # If statement to handle last chunk transfer if is > than filesize - if rangeStop.to_i > uploadFileHandle.size.to_i - contentRange = "bytes #{rangeStart.to_s}-#{uploadFileHandle.size.to_s}/#{uploadFileHandle.size.to_s}" - rangeLen = uploadFileHandle.size.to_i - rangeStart.to_i + if range_stop.to_i > upload_file_handle.size.to_i + content_range = "bytes #{range_start.to_s}-" + + "#{upload_file_handle.size.to_s}/" + + "#{upload_file_handle.size.to_s}" + range_len = upload_file_handle.size.to_i - range_start.to_i else - contentRange = "bytes #{rangeStart.to_s}-#{rangeStop.to_s}/#{uploadFileHandle.size.to_s}" - rangeLen = rangeStop.to_i - rangeStart.to_i + content_range = "bytes #{range_start.to_s}-" + + "#{range_stop.to_s}/" + + "#{upload_file_handle.size.to_s}" + range_len = range_stop.to_i - range_start.to_i end # Build headers extheader = { - 'x-vcloud-authorization' => @auth_key, - 'Content-Range' => contentRange, - 'Content-Length' => rangeLen.to_s + 'x-vcloud-authorization' => @auth_key, + 'Content-Range' => content_range, + 'Content-Length' => range_len.to_s } begin - uploadRequest = "#{@host_url}#{uploadURL}" - connection = clnt.request('PUT', uploadRequest, nil, fileContent, extheader) + upload_request = "#{@host_url}#{upload_url}" + # FIXME: Add debug on the return status of "connection" + # to enhance troubleshooting for this upload process. + # (tsugliani) + _connection = clnt.request( + 'PUT', + upload_request, + nil, + file_content, + extheader + ) - if config[:progressbar_enable] == true && uploadFileHandle.size.to_i > chunkSize + if config[:progressbar_enable] == true && + upload_file_handle.size.to_i > chunk_size params = { - 'method' => :get, - 'command' => "/vAppTemplate/vappTemplate-#{vAppTemplate}" + 'method' => :get, + 'command' => "/vAppTemplate/vappTemplate-#{vapp_template}" } - response, headers = send_request(params) + response, _headers = send_request(params) - response.css("Files File [name='#{fileName}']").each do |file| - progressbar.progress=file[:bytesTransferred].to_i + response.css( + "Files File [name='#{file_name}']" + ).each do |file| + progressbar.progress = file[:bytesTransferred].to_i end end - rescue # FIXME: HUGE FIXME!!!! DO SOMETHING WITH THIS, IT'S JUST STUPID AS IT IS NOW!!! - retryTime = (config[:retry_time] || 5) - puts "Range #{contentRange} failed to upload, retrying the chunk in #{retryTime.to_s} seconds, to stop the action press CTRL+C." - sleep retryTime.to_i + + rescue + # FIXME: HUGE FIXME!!!! + # DO SOMETHING WITH THIS, IT'S JUST STUPID AS IT IS NOW!!! + retry_time = (config[:retry_time] || 5) + puts "Range #{content_range} failed to upload, " + + "retrying the chunk in #{retry_time.to_s} seconds, " + + 'to stop this task press CTRL+C.' + sleep retry_time.to_i retry end end - uploadFileHandle.close + upload_file_handle.close end - ## # Convert vApp status codes into human readable description def convert_vapp_status(status_code) case status_code.to_i - when 0 - 'suspended' - when 3 - 'paused' - when 4 - 'running' - when 8 - 'stopped' - when 10 - 'mixed' - else - "Unknown #{status_code}" + when 0 + 'suspended' + when 3 + 'paused' + when 4 + 'running' + when 8 + 'stopped' + when 10 + 'mixed' + else + "Unknown #{status_code}" end end - end # class end end end \ No newline at end of file