diff --git a/ChangeLog.txt b/ChangeLog.txt index a398803d07..019d646eaf 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,3 +1,10 @@ +2014.05.06 - version 0.6.4 +* Upgraded Service Management Versioning to 2014-04-01 +* Created separate API for add role +* Logical Unit Number(lun) is optional argument in API add_data_disk +* Cloud service should delete only if there are no other VMs/Deployments in the cloud service +* Added more sizes(Basic_A0, Basic_A1, Basic_A2, Basic_A3, Basic_A4) options for Virtual Machine and Cloud Service. + 2014.03.28 - version 0.6.3 * Added get_cloud_service_properties method, which returns all cloud service properties (embed-detail=true), including info about all VMs * Added winrm_http_port and winrm_https_port to get_virtual_machine method to allow the users to configure custom ports for winrm-http and winrm-https diff --git a/README.md b/README.md index 13b345750d..ce34e566dc 100644 --- a/README.md +++ b/README.md @@ -374,13 +374,13 @@ virtual_machine_service.start_virtual_machine('vm_name', 'cloud_service_name') virtual_machine_service.restart_virtual_machine('vm_name', 'cloud_service_name') #API for add disk to Virtual Machine -lun = 1 #Valid LUN values are 0 through 15. options = { :disk_label => 'disk-label', :disk_size => 100, #In GB - :import => false + :import => false, + :disk_name => 'Disk name' #Required when import is true } -virtual_machine_service.add_data_disk('vm_name', 'cloud_service_name', lun, options) +virtual_machine_service.add_data_disk('vm_name', 'cloud_service_name', options) #API to add/update Virtual Machine endpoints endpoint1 = { @@ -422,19 +422,35 @@ options = { :private_key_file => 'c:/private_key.key', #required for ssh or winrm(https) certificate. :certificate_file => 'c:/certificate.pem', #required for ssh or winrm(https) certificate. :ssh_port => 2222, - :vm_size => 'Small', #valid choices are (ExtraSmall, Small, Medium, Large, ExtraLarge, A6, A7) + :vm_size => 'Small', #valid choices are (ExtraSmall Small Medium Large ExtraLarge A5 A6 A7 Basic_A0 Basic_A1 Basic_A2 Basic_A3 Basic_A4) :affinity_group_name => 'affinity1', :virtual_network_name => 'xplattestvnet', :subnet_name => 'subnet1', :availability_set_name => 'availabiltyset1' } -virtual_machine_service.create_virtual_machine(params,options,add_role=false) -# Here add_role is used as a flag to create multiple roles under the same cloud service. This parameter is false -# by default. Atleast a single deployment should be created under a hosted service prior to setting this flag. +virtual_machine_service.create_virtual_machine(params,options) #API usage to add new roles under cloud service creating VM - -virtual_machine_service.create_virtual_machine(params,options,add_role=true) +#API add_role create multiple roles under the same cloud service. Atleast a single deployment should be created under a hosted service. +params = { + :vm_name => 'vm_name', + :cloud_service_name => 'cloud_service_name', + :vm_user => 'azureuser', + :image => 'a699494373c04fc0bc8f2bb1389d6106__Win2K8R2SP1-Datacenter-201305.01-en.us-127GB.vhd', + :password => 'ComplexPassword', +} +options = { + :storage_account_name => 'storage_suse', + :winrm_transport => ['https','http'], #Currently http is supported. To enable https, set the transport protocol to https, simply rdp to the VM once VM is in ready state, export the certificate ( CN name would be the deployment name) from the certstore of the VM and install to your local machine and communicate WinRM via https. + :tcp_endpoints => '80,3389:3390', + :private_key_file => 'c:/private_key.key', #required for ssh or winrm(https) certificate. + :certificate_file => 'c:/certificate.pem', #required for ssh or winrm(https) certificate. + :winrm_https_port => 5999, + :winrm_http_port => 6999, #Used to open different powershell port + :vm_size => 'Small', #valid choices are (ExtraSmall Small Medium Large ExtraLarge A5 A6 A7 Basic_A0 Basic_A1 Basic_A2 Basic_A3 Basic_A4) + :availability_set_name => 'availabiltyset' +} +virtual_machine_service.add_role(params, options) #Get a list of available virtual machine images virtual_machine_image_service = Azure::VirtualMachineImageManagementService.new diff --git a/lib/azure/base_management/management_http_request.rb b/lib/azure/base_management/management_http_request.rb index 6119aac079..3be744334e 100644 --- a/lib/azure/base_management/management_http_request.rb +++ b/lib/azure/base_management/management_http_request.rb @@ -35,7 +35,7 @@ def initialize(method, path, body = nil) @warn = false content_length = body ? body.bytesize.to_s : '0' @headers = { - 'x-ms-version' => '2013-06-01', + 'x-ms-version' => '2014-04-01', 'Content-Type' => 'application/xml', 'Content-Length' => content_length } diff --git a/lib/azure/cloud_service_management/cloud_service.rb b/lib/azure/cloud_service_management/cloud_service.rb index ac300c0cc5..d604fe174f 100644 --- a/lib/azure/cloud_service_management/cloud_service.rb +++ b/lib/azure/cloud_service_management/cloud_service.rb @@ -16,7 +16,6 @@ module Azure module CloudServiceManagement class CloudService - def initialize yield self if block_given? end @@ -33,6 +32,7 @@ def initialize attr_accessor :extended_properties attr_accessor :default_winrm_certificate_thumbprint attr_accessor :virtual_machines + attr_accessor :deployment_name end end end diff --git a/lib/azure/cloud_service_management/cloud_service_management_service.rb b/lib/azure/cloud_service_management/cloud_service_management_service.rb index fa4a605aee..080285c0cf 100644 --- a/lib/azure/cloud_service_management/cloud_service_management_service.rb +++ b/lib/azure/cloud_service_management/cloud_service_management_service.rb @@ -17,7 +17,6 @@ module Azure module CloudServiceManagement class CloudServiceManagementService < BaseManagementService - def initialize super() end @@ -48,12 +47,12 @@ def initialize # # Returns None def create_cloud_service(name, options = {}) - Loggerx.error_with_exit "Cloud service name is not valid " unless name + Loggerx.error_with_exit 'Cloud service name is not valid ' unless name if get_cloud_service(name) Loggerx.warn "Cloud service #{name} already exists. Skipped..." else Loggerx.info "Creating cloud service #{name}." - request_path = "/services/hostedservices" + request_path = '/services/hostedservices' body = Serialization.cloud_services_to_xml(name, options) request = ManagementHttpRequest.new(:post, request_path, body) request.call @@ -64,7 +63,7 @@ def create_cloud_service(name, options = {}) # # Returns an array of Azure::CloudServiceManagement::CloudService objects def list_cloud_services - request_path = "/services/hostedservices" + request_path = '/services/hostedservices' request = ManagementHttpRequest.new(:get, request_path, nil) response = request.call Serialization.cloud_services_from_xml(response) @@ -95,7 +94,7 @@ def get_cloud_service_properties(name) request_path = "/services/hostedservices/#{name}?embed-detail=true" request = ManagementHttpRequest.new(:get, request_path) response = request.call - Serialization.cloud_services_from_xml(response) + Serialization.cloud_services_from_xml(response).first end # Public: Deletes the specified cloud service of given subscription id from Windows Azure. @@ -106,7 +105,7 @@ def get_cloud_service_properties(name) # # Returns: None def delete_cloud_service(cloud_service_name) - request_path= "/services/hostedservices/#{cloud_service_name}" + request_path = "/services/hostedservices/#{cloud_service_name}" request = ManagementHttpRequest.new(:delete, request_path) Loggerx.info "Deleting cloud service #{cloud_service_name}. \n" request.call @@ -122,7 +121,7 @@ def delete_cloud_service(cloud_service_name) # # Returns NONE def delete_cloud_service_deployment(cloud_service_name) - request_path= "/services/hostedservices/#{cloud_service_name}/deploymentslots/production" + request_path = "/services/hostedservices/#{cloud_service_name}/deploymentslots/production" request = ManagementHttpRequest.new(:delete, request_path) Loggerx.info "Deleting deployment of cloud service \"#{cloud_service_name}\" ..." request.call @@ -130,13 +129,12 @@ def delete_cloud_service_deployment(cloud_service_name) def upload_certificate(cloud_service_name, ssh) data = export_der(ssh[:cert], ssh[:key]) - request_path= "/services/hostedservices/#{cloud_service_name}/certificates" + request_path = "/services/hostedservices/#{cloud_service_name}/certificates" body = Serialization.add_certificate_to_xml(data) Loggerx.info "Uploading certificate to cloud service #{cloud_service_name}..." request = ManagementHttpRequest.new(:post, request_path, body) request.call end - end end -end \ No newline at end of file +end diff --git a/lib/azure/cloud_service_management/serialization.rb b/lib/azure/cloud_service_management/serialization.rb index 9e4ac25e06..dc978c2583 100644 --- a/lib/azure/cloud_service_management/serialization.rb +++ b/lib/azure/cloud_service_management/serialization.rb @@ -66,8 +66,10 @@ def self.cloud_services_from_xml(cloud_xml) cloud.label = Base64.decode64(xml_content(props_xml, 'Label')) cloud.description = xml_content(props_xml, 'Description') - cloud.location = xml_content(props_xml, 'Location') - cloud.affinity_group = xml_content(props_xml, 'AffinityGroup') + location = xml_content(props_xml, 'Location') + cloud.location = location unless location.empty? + affinity_group = xml_content(props_xml, 'AffinityGroup') + cloud.affinity_group = affinity_group unless affinity_group cloud.status = xml_content(props_xml, 'Status') cloud.date_created = xml_content(props_xml, 'DateCreated') cloud.date_modified = xml_content(props_xml, 'DateLastModified') @@ -82,7 +84,8 @@ def self.cloud_services_from_xml(cloud_xml) cloud.default_winrm_certificate_thumbprint = xml_content( cloud_service_xml, 'DefaultWinRMCertificateThumbprint' ) - + deployment_xml = cloud_services_xml.css('Deployments Deployment') + cloud.deployment_name = xml_content(deployment_xml, 'Name') vms_in_deployment = {} cloud_service_xml.css('Deployments').each do |deployxml| @@ -90,8 +93,7 @@ def self.cloud_services_from_xml(cloud_xml) vms = Azure::VirtualMachineManagement::Serialization.virtual_machines_from_xml( deployxml, cloud.name ) - - vms_in_deployment[deployment_name.to_sym] = vms + vms_in_deployment[deployment_name.to_sym] = vms if vms end cloud.virtual_machines = vms_in_deployment @@ -102,15 +104,14 @@ def self.cloud_services_from_xml(cloud_xml) def self.add_certificate_to_xml(data) builder = Nokogiri::XML::Builder.new do |xml| - xml.CertificateFile('xmlns'=>'http://schemas.microsoft.com/windowsazure') { + xml.CertificateFile('xmlns' => 'http://schemas.microsoft.com/windowsazure') do xml.Data data xml.CertificateFormat 'pfx' xml.Password nil - } + end end builder.doc.to_xml end - end end end diff --git a/lib/azure/storage_management/storage_management_service.rb b/lib/azure/storage_management/storage_management_service.rb index 3c3ac2ef03..3619d1e79d 100644 --- a/lib/azure/storage_management/storage_management_service.rb +++ b/lib/azure/storage_management/storage_management_service.rb @@ -55,13 +55,13 @@ def get_storage_account(name) end # Public: Gets the properties of the storage account specified. - # + # # ==== Attributes # # * +name+ - String. The name of the storage account. Required. # # See http://msdn.microsoft.com/en-us/library/windowsazure/ee460802.aspx - # + # # Returns the storage account def get_storage_account_properties(name) request_path = "/services/storageservices/#{name}" diff --git a/lib/azure/version.rb b/lib/azure/version.rb index d89abbbbdd..faa8d28249 100644 --- a/lib/azure/version.rb +++ b/lib/azure/version.rb @@ -17,7 +17,7 @@ module Azure class Version MAJOR = 0 unless defined? MAJOR MINOR = 6 unless defined? MINOR - UPDATE = 3 unless defined? UPDATE + UPDATE = 4 unless defined? UPDATE PRE = nil unless defined? PRE class << self diff --git a/lib/azure/virtual_machine_image_management/serialization.rb b/lib/azure/virtual_machine_image_management/serialization.rb index dceb52ba00..fc5e75b254 100644 --- a/lib/azure/virtual_machine_image_management/serialization.rb +++ b/lib/azure/virtual_machine_image_management/serialization.rb @@ -18,9 +18,8 @@ module Azure module VirtualMachineImageManagement module Serialization - def self.virtual_machine_images_from_xml(imageXML) - os_images = Array.new + os_images = [] virtual_machine_images = imageXML.css('Images OSImage') virtual_machine_images.each do |image_node| image = VirtualMachineImage.new @@ -34,20 +33,19 @@ def self.virtual_machine_images_from_xml(imageXML) end def self.disks_from_xml(diskXML) - os_disks = Array.new + os_disks = [] disks = diskXML.css('Disks Disk') disks.each do |disk_node| disk = VirtualMachineDisk.new disk.name = xml_content(disk_node, 'Name') disk.os_type = xml_content(disk_node, 'OS') - disk.attached = !xml_content(disk_node,'AttachedTo').empty? + disk.attached = !xml_content(disk_node, 'AttachedTo').empty? disk.image = xml_content(disk_node, 'SourceImageName') disk.size = xml_content(disk_node, 'LogicalDiskSizeInGB') os_disks << disk end os_disks end - end end -end \ No newline at end of file +end diff --git a/lib/azure/virtual_machine_image_management/virtual_machine_disk.rb b/lib/azure/virtual_machine_image_management/virtual_machine_disk.rb index d13ebb4b10..9568e87514 100644 --- a/lib/azure/virtual_machine_image_management/virtual_machine_disk.rb +++ b/lib/azure/virtual_machine_image_management/virtual_machine_disk.rb @@ -15,13 +15,11 @@ module Azure module VirtualMachineImageManagement class VirtualMachineDisk - def initialize yield self if block_given? end attr_accessor :name, :attached, :os_type, :image, :size - end end end diff --git a/lib/azure/virtual_machine_image_management/virtual_machine_image.rb b/lib/azure/virtual_machine_image_management/virtual_machine_image.rb index 63b25b4d7a..d7e0b66c93 100644 --- a/lib/azure/virtual_machine_image_management/virtual_machine_image.rb +++ b/lib/azure/virtual_machine_image_management/virtual_machine_image.rb @@ -15,13 +15,11 @@ module Azure module VirtualMachineImageManagement class VirtualMachineImage - def initialize yield self if block_given? end attr_accessor :os_type, :name, :category, :locations - end end end diff --git a/lib/azure/virtual_machine_image_management/virtual_machine_image_management_service.rb b/lib/azure/virtual_machine_image_management/virtual_machine_image_management_service.rb index bdbab164db..8565c1ad1a 100644 --- a/lib/azure/virtual_machine_image_management/virtual_machine_image_management_service.rb +++ b/lib/azure/virtual_machine_image_management/virtual_machine_image_management_service.rb @@ -12,12 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- -require "azure/virtual_machine_image_management/serialization" +require 'azure/virtual_machine_image_management/serialization' module Azure module VirtualMachineImageManagement class VirtualMachineImageManagementService < BaseManagementService - def initialize super() end @@ -26,16 +25,14 @@ def initialize # # Returns an array of Azure::VirtualMachineImageManagementService objects def list_virtual_machine_images - request_path = "/services/images" + request_path = '/services/images' request = ManagementHttpRequest.new(:get, request_path, nil) response = request.call Serialization.virtual_machine_images_from_xml(response) end - end class VirtualMachineDiskManagementService < BaseManagementService - def initialize super() end @@ -44,14 +41,14 @@ def initialize # # Returns an array of Azure::VirtualMachineDiskManagementService objects def list_virtual_machine_disks - request_path = "/services/disks" + request_path = '/services/disks' request = ManagementHttpRequest.new(:get, request_path, nil) response = request.call Serialization.disks_from_xml(response) end def get_virtual_machine_disk(disk_name) - disk = list_virtual_machine_disks.select {|x| x.name == disk_name} + disk = list_virtual_machine_disks.select { |x| x.name == disk_name } disk.first end @@ -64,8 +61,6 @@ def delete_virtual_machine_disk(disk_name) request = ManagementHttpRequest.new(:delete, path) request.call end - end end end - diff --git a/lib/azure/virtual_machine_management/serialization.rb b/lib/azure/virtual_machine_management/serialization.rb index b746947161..b9bba829d4 100644 --- a/lib/azure/virtual_machine_management/serialization.rb +++ b/lib/azure/virtual_machine_management/serialization.rb @@ -263,6 +263,7 @@ def self.virtual_machines_from_xml(deployXML, cloud_service_name) if xml_content(role, 'RoleName') == role_name vm.availability_set_name = xml_content(role, 'AvailabilitySetName') endpoints_from_xml(role, vm) + vm.data_disks = data_disks_from_xml(role) vm.os_type = xml_content(role, 'OSVirtualHardDisk OS') vm.disk_name = xml_content(role, 'OSVirtualHardDisk DiskName') vm.media_link = xml_content(role, 'OSVirtualHardDisk MediaLink') @@ -276,6 +277,20 @@ def self.virtual_machines_from_xml(deployXML, cloud_service_name) end end + def self.data_disks_from_xml(rolesXML) + data_disks = [] + virtual_hard_disks = rolesXML.css('DataVirtualHardDisks DataVirtualHardDisk') + virtual_hard_disks.each do |disk| + data_disk = {} + data_disk[:name] = xml_content(disk, 'DiskName') + data_disk[:lun] = xml_content(disk, 'Lun') + data_disk[:size_in_gb] = xml_content(disk, 'LogicalDiskSizeInGB') + data_disk[:media_link] = xml_content(disk, 'MediaLink') + data_disks << data_disk + end + data_disks + end + def self.endpoints_from_xml(rolesXML, vm) vm.tcp_endpoints = [] vm.udp_endpoints = [] @@ -370,10 +385,11 @@ def self.endpoints_to_xml(xml, endpoints) end end - def self.add_data_disk_to_xml(lun, media_link, options) + def self.add_data_disk_to_xml(vm, options) if options[:import] && options[:disk_name].nil? Loggerx.error_with_exit "The data disk name is not valid." end + media_link = vm.media_link builder = Nokogiri::XML::Builder.new do |xml| xml.DataVirtualHardDisk( 'xmlns' => 'http://schemas.microsoft.com/windowsazure', @@ -382,8 +398,7 @@ def self.add_data_disk_to_xml(lun, media_link, options) xml.HostCaching options[:host_caching] || 'ReadOnly' xml.DiskLabel options[:disk_label] xml.DiskName options[:disk_name] if options[:import] - xml.Lun lun - xml.LogicalDiskSizeInGB options[:disk_size] || 1 + xml.LogicalDiskSizeInGB options[:disk_size] || 100 unless options[:import] disk_name = media_link[/([^\/]+)$/] media_link = media_link.gsub(/#{disk_name}/, (Time.now.strftime('disk_%Y_%m_%d_%H_%M')) + '.vhd') @@ -399,7 +414,7 @@ def self.add_data_disk_to_xml(lun, media_link, options) def self.port_already_opened?(existing_ports, port) return false if existing_ports.nil? raise "Port #{port} conflicts with a port already opened. "\ - "Please select a different port." if existing_ports.include?(port) + "Please select a different port." if existing_ports.include?(port) false end diff --git a/lib/azure/virtual_machine_management/virtual_machine.rb b/lib/azure/virtual_machine_management/virtual_machine.rb index d4e91fabc7..241def4c54 100644 --- a/lib/azure/virtual_machine_management/virtual_machine.rb +++ b/lib/azure/virtual_machine_management/virtual_machine.rb @@ -36,6 +36,7 @@ def initialize attr_accessor :virtual_network_name attr_accessor :availability_set_name attr_accessor :media_link + attr_accessor :data_disks end end end diff --git a/lib/azure/virtual_machine_management/virtual_machine_management_service.rb b/lib/azure/virtual_machine_management/virtual_machine_management_service.rb index 931e97b62a..1b30ae37be 100644 --- a/lib/azure/virtual_machine_management/virtual_machine_management_service.rb +++ b/lib/azure/virtual_machine_management/virtual_machine_management_service.rb @@ -51,7 +51,7 @@ def list_virtual_machines(*cloud_service_names) # # Returns an Azure::VirtualMachineManagement::VirtualMachine instance. def get_virtual_machine(name, cloud_service_name) - server = list_virtual_machines(cloud_service_name).select { |x| x.vm_name == name && x.cloud_service_name == cloud_service_name } + server = list_virtual_machines(cloud_service_name).select { |x| x.vm_name == name.downcase } server.first end @@ -103,57 +103,33 @@ def get_virtual_machine(name, cloud_service_name) # See: # http://msdn.microsoft.com/en-us/library/windowsazure/jj157194.aspx # http://msdn.microsoft.com/en-us/library/windowsazure/jj157186.aspx - def create_virtual_machine(params, options = {}, add_role = false) + def create_virtual_machine(params, options = {}) options[:os_type] = get_os_type(params[:image]) validate_deployment_params(params, options) options[:deployment_name] ||= options[:cloud_service_name] - - unless add_role - Loggerx.info 'Creating deploymnent...' - options[:cloud_service_name] ||= generate_cloud_service_name(params[:vm_name]) - options[:storage_account_name] ||= generate_storage_account_name(params[:vm_name]) - optionals = {} - if options[:virtual_network_name] - virtual_network_service = Azure::VirtualNetworkManagementService.new - virtual_networks = virtual_network_service.list_virtual_networks.select { |x| x.name == options[:virtual_network_name] } - if virtual_networks.empty? - Loggerx.error_with_exit "Virtual network #{options[:virtual_network_name]} doesn't exists" - else - optionals[:affinity_group_name] = virtual_networks.first.affinity_group - end - elsif options[:affinity_group_name] - optionals[:affinity_group_name] = options[:affinity_group_name] + Loggerx.info 'Creating deploymnent...' + options[:cloud_service_name] ||= generate_cloud_service_name(params[:vm_name]) + options[:storage_account_name] ||= generate_storage_account_name(params[:vm_name]) + optionals = {} + if options[:virtual_network_name] + virtual_network_service = Azure::VirtualNetworkManagementService.new + virtual_networks = virtual_network_service.list_virtual_networks.select { |x| x.name == options[:virtual_network_name] } + if virtual_networks.empty? + Loggerx.error_with_exit "Virtual network #{options[:virtual_network_name]} doesn't exists" else - optionals[:location] = params[:location] + optionals[:affinity_group_name] = virtual_networks.first.affinity_group end - cloud_service = Azure::CloudServiceManagementService.new - cloud_service.create_cloud_service(options[:cloud_service_name], optionals) - cloud_service.upload_certificate(options[:cloud_service_name], params[:certificate]) unless params[:certificate].empty? - Azure::StorageManagementService.new.create_storage_account(options[:storage_account_name], optionals) - body = Serialization.deployment_to_xml(params, options) - path = "/services/hostedservices/#{options[:cloud_service_name]}/deployments" + elsif options[:affinity_group_name] + optionals[:affinity_group_name] = options[:affinity_group_name] else - cloud_services = Azure::CloudServiceManagementService.new.get_cloud_service_properties( - options[:cloud_service_name] - ) - existing_ports = [] - # There should be only one cloud_serivce in the Array. - cloud_services.each do |cloud_service| - cloud_service.virtual_machines[options[:deployment_name].to_sym].each do |vm| - vm.tcp_endpoints.each do |endpoint| - existing_ports << endpoint[:public_port] - end - end - end - - options[:existing_ports] = existing_ports - - Loggerx.info 'Deployment exists, adding role...' - body = Serialization.role_to_xml(params, options).to_xml - path = "/services/hostedservices/#{options[:cloud_service_name]}/deployments/#{options[:deployment_name]}/roles" + optionals[:location] = params[:location] end - Loggerx.error 'Cloud service name is required for adding role.' unless options[:cloud_service_name] - Loggerx.error 'Storage account name is required for adding role.' unless options[:storage_account_name] + cloud_service = Azure::CloudServiceManagementService.new + cloud_service.create_cloud_service(options[:cloud_service_name], optionals) + cloud_service.upload_certificate(options[:cloud_service_name], params[:certificate]) unless params[:certificate].empty? + Azure::StorageManagementService.new.create_storage_account(options[:storage_account_name], optionals) + body = Serialization.deployment_to_xml(params, options) + path = "/services/hostedservices/#{options[:cloud_service_name]}/deployments" Loggerx.info 'Deployment in progress...' request = ManagementHttpRequest.new(:post, path, body) request.call @@ -162,6 +138,71 @@ def create_virtual_machine(params, options = {}, add_role = false) e.message end + # Public: Add a new role to a cloud service. Atleast one deployment should exist before you can add a role. + # + # ==== Attributes + # + # * +params+ - Hash. parameters. + # * +options+ - Hash. Optional parameters. + # + # ==== Params + # + # Accepted key/value pairs are: + # * +:vm_name+ - String. Name of virtual machine. + # * +:vm_user+ - String. User name for the virtual machine instance. + # * +:password+ - String. A description for the hosted service. + # * +:image+ - String. Name of the disk image to use to create the virtual machine. + # * +:cloud_service_name+ - String. Name of cloud service. + # + # ==== Options + # + # Accepted key/value pairs are: + # * +:storage_account_name+ - String. Name of storage account. + # * +:tcp_endpoints+ - String. Specifies the internal port and external/public port separated by a colon. + # You can map multiple internal and external ports by separating them with a comma. + # * +:ssh_private_key_file+ - String. Path of private key file. + # * +:ssh_certificate_file+ - String. Path of certificate file. + # * +:ssh_port+ - Integer. Specifies the SSH port number. + # * +:winrm_http_port - Integer. Specifies the WinRM HTTP port number. + # * +:winrm_https_port - Integer. Specifies the WinRM HTTPS port number. + # * +:vm_size+ - String. Specifies the size of the virtual machine instance. + # * +:winrm_transport+ - Array. Specifies WINRM transport protocol. + # + # Returns Azure::VirtualMachineManagement::VirtualMachine objects of newly created instance. + # + # See: + # http://msdn.microsoft.com/en-us/library/windowsazure/jj157186.aspx + def add_role(params, options = {}) + options[:os_type] = get_os_type(params[:image]) + validate_deployment_params(params, options, true) + cloud_service = Azure::CloudServiceManagementService.new + cloud_service = cloud_service.get_cloud_service_properties(params[:cloud_service_name]) + deployment_name = cloud_service.deployment_name + Loggerx.error_with_exit "Deployment doesn't exists." if cloud_service && deployment_name.empty? + others = {} + if cloud_service.location + others[:location] = cloud_service.location + elsif cloud_service.affinity_group + others[:affinity_group_name] = cloud_service.affinity_group + end + options[:storage_account_name] ||= generate_storage_account_name(params[:vm_name]) + Azure::StorageManagementService.new.create_storage_account(options[:storage_account_name], others) + Loggerx.info 'Deployment exists, adding role...' + existing_ports = [] + cloud_service.virtual_machines[deployment_name.to_sym].each do |vm| + vm.tcp_endpoints.each do |endpoint| + existing_ports << endpoint[:public_port] + end + end + options[:existing_ports] = existing_ports + body = Serialization.role_to_xml(params, options).to_xml + path = "/services/hostedservices/#{cloud_service.name}/deployments/#{deployment_name}/roles" + Loggerx.info 'Deployment in progress...' + request = ManagementHttpRequest.new(:post, path, body) + request.call + get_virtual_machine(params[:vm_name], cloud_service.name) + end + # Public: Deletes the deployment, cloud service and disk. # # ==== Attributes @@ -174,11 +215,19 @@ def create_virtual_machine(params, options = {}, add_role = false) # # Returns NONE def delete_virtual_machine(vm_name, cloud_service_name) - vm = get_virtual_machine(vm_name, cloud_service_name) + virtual_machines = list_virtual_machines(cloud_service_name) + vm = virtual_machines.select { |x| x.vm_name == vm_name }.first if vm - cloud_service = Azure::CloudServiceManagementService.new - cloud_service.delete_cloud_service_deployment(cloud_service_name) - cloud_service.delete_cloud_service(cloud_service_name) + if virtual_machines.size == 1 + cloud_service = Azure::CloudServiceManagementService.new + cloud_service.delete_cloud_service_deployment(cloud_service_name) + cloud_service.delete_cloud_service(cloud_service_name) + else + path = "/services/hostedservices/#{vm.cloud_service_name}/deployments/#{vm.deployment_name}/roles/#{vm.vm_name}" + Loggerx.info "Deleting virtual machine #{vm_name}. \n" + request = ManagementHttpRequest.new(:delete, path) + request.call + end Loggerx.info "Waiting for disk to be released.\n" disk_name = vm.disk_name disk_management_service = VirtualMachineDiskManagementService.new @@ -217,7 +266,7 @@ def delete_virtual_machine(vm_name, cloud_service_name) def shutdown_virtual_machine(vm_name, cloud_service_name) vm = get_virtual_machine(vm_name, cloud_service_name) if vm - if ['StoppedVM', 'StoppedDeallocated'].include?(vm.status) + if %w(StoppedVM StoppedDeallocated).include?(vm.status) Loggerx.error 'Cannot perform the shutdown operation on a stopped virtual machine.' elsif vm.deployment_status == 'Running' path = "/services/hostedservices/#{vm.cloud_service_name}/deployments/#{vm.deployment_name}/roleinstances/#{vm.vm_name}/Operations" @@ -282,7 +331,7 @@ def restart_virtual_machine(vm_name, cloud_service_name) Loggerx.error "Cannot find virtual machine \"#{vm_name}\" under cloud service \"#{cloud_service_name}\"." end end - + # Public: Add/Update endpoints of virtual machine. # # ==== Attributes @@ -304,7 +353,7 @@ def restart_virtual_machine(vm_name, cloud_service_name) # * +:protocol+ - String. Specifies the transport protocol # for the endpoint. Possible values are: TCP, UDP # * +:direct_server_return+ - String. Specifies whether the endpoint - # uses Direct Server Return. (optional) + # uses Direct Server Return. Possible values are: true, false (optional) # * +:load_balancer - Hash. Contains properties that define the # endpoint settings that the load balancer uses to monitor the # availability of the Virtual Machine (optional) @@ -333,7 +382,7 @@ def update_endpoints(vm_name, cloud_service_name, *input_endpoints) path = "/services/hostedservices/#{vm.cloud_service_name}/deployments/#{vm.deployment_name}/roles/#{vm_name}" endpoints = vm.tcp_endpoints + vm.udp_endpoints input_endpoints.each do |iep| - endpoints.delete_if { |ep| iep[:name].downcase == ep[:name].downcase && iep[:protocol].downcase == ep[:protocol] } + endpoints.delete_if { |ep| iep[:name].downcase == ep[:name].downcase } end endpoints += input_endpoints body = Serialization.update_role_to_xml(endpoints, vm) @@ -352,7 +401,7 @@ def update_endpoints(vm_name, cloud_service_name, *input_endpoints) # * +name+ - String. Virtual machine name. # * +cloud_service_name+ - String. Cloud service name. # * +endpoint_name+ - String. Name of endpoint. - # + # # See http://msdn.microsoft.com/en-us/library/windowsazure/jj157187.aspx # # Returns NONE @@ -377,8 +426,6 @@ def delete_endpoint(vm_name, cloud_service_name, endpoint_name) # # * +cloud_service_name+ - String. Cloud service name. # * +vm_name+ - String. Virtual machine name. - # * +lun+ - String. Specifies the Logical Unit Number - # (LUN) for the disk. Valid LUN values are 0 through 15. # * +options+ - Hash. Optional parameters. # # ==== Options @@ -396,12 +443,12 @@ def delete_endpoint(vm_name, cloud_service_name, endpoint_name) # See http://msdn.microsoft.com/en-us/library/windowsazure/jj157199.aspx # # Returns None - def add_data_disk(vm_name, cloud_service_name, lun, options = {}) + def add_data_disk(vm_name, cloud_service_name, options = {}) options[:import] ||= false vm = get_virtual_machine(vm_name, cloud_service_name) if vm path = "/services/hostedservices/#{cloud_service_name}/deployments/#{vm.deployment_name}/roles/#{vm_name}/DataDisks" - body = Serialization.add_data_disk_to_xml(lun, vm.media_link, options) + body = Serialization.add_data_disk_to_xml(vm, options) Loggerx.info "Adding data disk to virtual machine #{vm_name} ..." request = ManagementHttpRequest.new(:post, path, body) request.call @@ -430,15 +477,17 @@ def generate_storage_account_name(vm_name) random_string(vm_name + 'storage').gsub(/[^0-9a-z ]/i, '').downcase[0..23] end - def validate_deployment_params(params, options) + def validate_deployment_params(params, options, add_role = false) errors = [] - params_keys = ['vm_name', 'image', 'location', 'vm_user'] - if options[:os_type] == 'Windows' - params_keys += ['password'] - end + params_keys = %w(vm_name image vm_user) + params_keys += ['password'] if options[:os_type] == 'Windows' options_keys = [] - options_keys = ['private_key_file', 'certificate_file'] if certificate_required?(params, options) - + options_keys = %w(private_key_file certificate_file) if certificate_required?(params, options) + if add_role + params_keys += ['cloud_service_name'] + else + params_keys += ['location'] + end params_keys.each do |key| errors << key if params[key.to_sym].nil? end @@ -446,9 +495,10 @@ def validate_deployment_params(params, options) options_keys.each do |key| errors << key if options[key.to_sym].nil? end - validate_role_size(options[:vm_size]) - validate_location(params[:location]) unless errors.include?('location') + if errors.empty? + validate_location(params[:location]) unless add_role + validate_role_size(options[:vm_size]) params[:certificate] = {} if certificate_required?(params, options) begin @@ -479,7 +529,7 @@ def winrm_with_https(options) end def validate_role_size(vm_size) - valid_role_sizes = ['ExtraSmall', 'Small', 'Medium', 'Large', 'ExtraLarge', 'A6', 'A7'] + valid_role_sizes = %w(ExtraSmall Small Medium Large ExtraLarge A5 A6 A7 Basic_A0 Basic_A1 Basic_A2 Basic_A3 Basic_A4) if vm_size && !valid_role_sizes.include?(vm_size) Loggerx.error_with_exit "Value '#{vm_size}' specified for parameter 'vm_size' is invalid. Allowed values are 'ExtraSmall,Small,Medium,Large,ExtraLarge,A6,A7'" end diff --git a/test/integration/cloud_service/Cloud_Create_test.rb b/test/integration/cloud_service/Cloud_Create_test.rb new file mode 100644 index 0000000000..a370c21fd0 --- /dev/null +++ b/test/integration/cloud_service/Cloud_Create_test.rb @@ -0,0 +1,44 @@ +#------------------------------------------------------------------------- +# Copyright 2013 Microsoft Open Technologies, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# 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 'integration/test_helper' + +describe Azure::CloudServiceManagementService do + + subject { Azure::CloudServiceManagementService.new } + let(:options) do + { + location: 'West US', + description: 'Test' + } + end + + before do + Loggerx.expects(:puts).returns(nil).at_least(0) + end + + describe '#create_cloud_service' do + before do + @cloud_name = random_string('test-service-cloud', 10) + subject.create_cloud_service(@cloud_name, options) + end + + it 'Creates a new cloud service in Windows Azure.' do + cloud_service = subject.get_cloud_service_properties(@cloud_name) + assert cloud_service.name, @cloud_name + assert cloud_service.location, options[:location] + assert cloud_service.virtual_machines, Hash.new + end + end +end diff --git a/test/integration/cloud_service/Cloud_Delete_test.rb b/test/integration/cloud_service/Cloud_Delete_test.rb new file mode 100644 index 0000000000..d12863e920 --- /dev/null +++ b/test/integration/cloud_service/Cloud_Delete_test.rb @@ -0,0 +1,44 @@ +#------------------------------------------------------------------------- +# Copyright 2013 Microsoft Open Technologies, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# 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 'integration/test_helper' + +describe Azure::CloudServiceManagementService do + + subject { Azure::CloudServiceManagementService.new } + + let(:options) do + { + location: 'West US', + description: 'Test' + } + end + + before do + Loggerx.expects(:puts).returns(nil).at_least(0) + end + + describe '#delete_cloud_service' do + before do + @cloud_name = random_string('test-service-cloud', 10) + subject.create_cloud_service(@cloud_name, options) + end + + it 'Deletes the cloud service in Windows Azure.' do + subject.delete_cloud_service(@cloud_name) + present = subject.get_cloud_service(@cloud_name) + assert_equal present, false + end + end +end diff --git a/test/integration/vm/VM_Create_test.rb b/test/integration/vm/VM_Create_test.rb index ff96f3e3c8..c918e47ba9 100644 --- a/test/integration/vm/VM_Create_test.rb +++ b/test/integration/vm/VM_Create_test.rb @@ -50,7 +50,7 @@ { storage_account_name: storage_account_name, cloud_service_name: cloud_service_name, - vm_size: 'Small' + vm_size: 'Basic_A0' } end @@ -67,27 +67,73 @@ describe '#deployment' do + describe '#add_role' do + before do + @vm_obj = subject.create_virtual_machine(params, options) + params[:cloud_service_name] = options[:cloud_service_name] + options.delete(:cloud_service_name) + params.delete(:location) + params[:vm_name] = "add-#{virtual_machine_name}" + sleep 30 + end + + it 'should add role to existing storage account and cloud service' do + vm = subject.add_role(params, options) + vm.cloud_service_name.must_equal params[:cloud_service_name] + vm.vm_name.must_equal params[:vm_name] + vm.deployment_name.must_equal @vm_obj.deployment_name + vm.os_type.must_equal 'Linux' + end + + it 'should add role and create new storage account' do + params[:vm_name] = "Add-storage-#{virtual_machine_name}" + vm = subject.add_role(params) + vm.cloud_service_name.must_equal params[:cloud_service_name] + vm.vm_name.must_equal params[:vm_name].downcase + vm.deployment_name.must_equal @vm_obj.deployment_name + end + end + + describe '#virtual_network' do + before do + options[:virtual_network_name] = 'v-net' + affinity_gorup_name = random_string('affinity-group-', 10) + Azure::BaseManagementService.new.create_affinity_group( + affinity_gorup_name, + params[:location], + 'AG1' + ) rescue nil + vnet_service = Azure::VirtualNetworkManagementService + vnet_service.new.set_network_configuration( + options[:virtual_network_name], + affinity_gorup_name, + ['172.16.0.0/12'] + ) rescue nil + subject.create_virtual_machine(params, options) + end + + it 'should provision virtual machine in a existing virtual network' do + virtual_machine = subject.get_virtual_machine(virtual_machine_name, cloud_service_name) + virtual_machine.must_be_kind_of Azure::VirtualMachineManagement::VirtualMachine + virtual_machine.vm_name.must_equal virtual_machine_name + virtual_machine.virtual_network_name.must_equal options[:virtual_network_name] + end + end + it 'should set options hash with valid cloud_service_name, deployment_name, storage_account_name and virtual network' do csn = options[:cloud_service_name] options[:availability_set_name] = 'aval-set-test' - vm = subject.create_virtual_machine(params, options, false) + vm = subject.create_virtual_machine(params, options) vm.must_be_kind_of Azure::VirtualMachineManagement::VirtualMachine vm.cloud_service_name.wont_be_nil vm.vm_name.must_equal virtual_machine_name vm.deployment_name.wont_be_nil vm.deployment_name.must_equal vm.cloud_service_name vm.os_type.must_equal 'Linux' - vm.role_size.must_equal 'Small' + vm.role_size.must_equal 'Basic_A0' vm.availability_set_name.must_equal 'aval-set-test' options[:storage_account_name].wont_be_nil assert_match(/^#{params[:vm_name] + '-service'}*/, csn) - # Test for add role - params[:vm_name] = 'test-add-role-vm' - vm = subject.create_virtual_machine(params, options, true) - vm.cloud_service_name.must_equal csn - vm.vm_name.must_equal params[:vm_name] - vm.deployment_name.wont_be_nil - vm.os_type.must_equal 'Linux' end it 'should creates http and https enabled winrm virtual machine without certificate.' do diff --git a/test/integration/vm/VM_Operations_test.rb b/test/integration/vm/VM_Operations_test.rb index 75a1fe2b80..285d243e46 100644 --- a/test/integration/vm/VM_Operations_test.rb +++ b/test/integration/vm/VM_Operations_test.rb @@ -64,7 +64,7 @@ before do subject.shutdown_virtual_machine(vm_name, csn) end - + it 'starts virtual machine' do subject.start_virtual_machine(vm_name, csn) vm = subject.get_virtual_machine(vm_name, csn) @@ -107,9 +107,8 @@ describe '#add_data_disk' do it 'add data disk to virtual machine' do - lun = rand(1..15) others = { disk_size: 100 } - subject.add_data_disk(vm_name, csn , lun, others) + subject.add_data_disk(vm_name, csn, others) dms = VirtualMachineDiskManagementService.new disks = dms.list_virtual_machine_disks disks = disks.select { |x| (/#{csn}/ =~ x.name) && x.os_type.empty? } @@ -119,7 +118,7 @@ end describe 'Add, Update, Delete endpoints' do - + it 'should add endpoints to virtual machine.' do ep1 = { name: 'endpoint-1', diff --git a/test/unit/cloud_service_management/cloud_service_management_service_test.rb b/test/unit/cloud_service_management/cloud_service_management_service_test.rb index 527e088268..84f96e47f6 100644 --- a/test/unit/cloud_service_management/cloud_service_management_service_test.rb +++ b/test/unit/cloud_service_management/cloud_service_management_service_test.rb @@ -12,73 +12,73 @@ # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- -require "test_helper" +require 'test_helper' describe Azure::CloudServiceManagementService do subject { Azure::CloudServiceManagementService.new } - let(:request_path) {'/services/hostedservices'} - let(:cloud_services_xml) { Fixtures["list_cloud_services"] } + let(:request_path) { '/services/hostedservices' } + let(:cloud_services_xml) { Fixtures['list_cloud_services'] } let(:method) { :get } - let(:mock_request){ mock() } - let(:response) { - response = mock() + let(:mock_request) { mock } + let(:response) do + response = mock response.stubs(:body).returns(cloud_services_xml) response - } + end let(:response_body) { Nokogiri::XML response.body } - before{ + before do Loggerx.expects(:puts).returns(nil).at_least(0) - } + end - describe "#list_cloud_services" do - before { + describe '#list_cloud_services' do + before do ManagementHttpRequest.stubs(:new).with(method, request_path, nil).returns(mock_request) mock_request.expects(:call).returns(response_body) - } - - it "assembles a URI for the request" do + end + + it 'assembles a URI for the request' do subject.list_cloud_services end - - it "sets the properties of the CloudService instance" do + + it 'sets the properties of the CloudService instance' do cloud_service = subject.list_cloud_services.first cloud_service.name.must_equal 'cloud-service-1' end - - it "returns a list of cloud services for the subscription" do + + it 'returns a list of cloud services for the subscription' do results = subject.list_cloud_services results.must_be_kind_of Array results.length.must_equal 2 results.first.must_be_kind_of Azure::CloudServiceManagement::CloudService end end - - describe "#get_cloud_service" do - before { + + describe '#get_cloud_service' do + before do ManagementHttpRequest.stubs(:new).with(method, request_path, nil).returns(mock_request) mock_request.expects(:call).returns(response_body) - } - - it "assembles a URI for the request" do + end + + it 'assembles a URI for the request' do subject.get_cloud_service 'cloud-service-1' end - - it "returns true if found cloud service with given name" do + + it 'returns true if found cloud service with given name' do result = subject.get_cloud_service 'cloud-service-1' result.must_equal true end - + it "returns false if cloud service with given name doesn't exists" do result = subject.get_cloud_service 'cloud-service-3' result.must_equal false end end - describe "#create_cloud_service" do - - it "Create cloud service return message if cloud service exists of given name." do + describe '#create_cloud_service' do + + it 'Create cloud service return message if cloud service exists of given name.' do ManagementHttpRequest.any_instance.expects(:call).returns response_body msg = subject.create_cloud_service 'cloud-service-1' assert_match(/^Cloud service cloud-service-1 already exists*/, msg) diff --git a/test/unit/cloud_service_management/serialization_test.rb b/test/unit/cloud_service_management/serialization_test.rb index 097ffc79ad..5394c1f71b 100644 --- a/test/unit/cloud_service_management/serialization_test.rb +++ b/test/unit/cloud_service_management/serialization_test.rb @@ -59,7 +59,7 @@ end describe '#cloud_services_to_xml' do - let(:cloud_service_name) {'cloud-service'} + let(:cloud_service_name) { 'cloud-service' } it 'accepts an name and options hash' do subject.cloud_services_to_xml cloud_service_name @@ -68,9 +68,9 @@ it 'uses name for label if label not provided' do results = subject.cloud_services_to_xml( cloud_service_name, - { - location: 'East Asia' - } + + location: 'East Asia' + ) doc = Nokogiri::XML(results) @@ -80,10 +80,10 @@ it 'uses label when label is provided' do results = subject.cloud_services_to_xml( cloud_service_name, - { - location: 'East Asia', - label: 'My Label' - } + + location: 'East Asia', + label: 'My Label' + ) doc = Nokogiri::XML(results) @@ -92,7 +92,7 @@ it 'serializes the argument to xml' do results = subject.cloud_services_to_xml( - cloud_service_name, { location: 'West US' } + cloud_service_name, location: 'West US' ) results.must_be_kind_of String doc = Nokogiri::XML results @@ -103,10 +103,10 @@ it 'uses affinity_group if provided and ignores location' do results = subject.cloud_services_to_xml( cloud_service_name, - { - affinity_group_name: 'my-affinity-group', - location: 'West US' - } + + affinity_group_name: 'my-affinity-group', + location: 'West US' + ) results.must_be_kind_of String doc = Nokogiri::XML results @@ -119,9 +119,9 @@ it 'uses location when affinity group not provided' do results = subject.cloud_services_to_xml( cloud_service_name, - { - location: 'East Asia' - } + + location: 'East Asia' + ) doc = Nokogiri::XML(results) @@ -137,9 +137,9 @@ } results = subject.cloud_services_to_xml( cloud_service_name, - { - extended_properties: xtended_props - } + + extended_properties: xtended_props + ) doc = Nokogiri::XML(results) @@ -159,7 +159,7 @@ it 'serializes the options hash to xml' do results = subject.cloud_services_to_xml( cloud_service_name, - { location: 'East US' } + location: 'East US' ) doc = Nokogiri::XML results doc.css('Location').text.must_equal 'East US' diff --git a/test/unit/virtual_machine_image_management/serialization_test.rb b/test/unit/virtual_machine_image_management/serialization_test.rb index 10282379f8..faa765c36d 100644 --- a/test/unit/virtual_machine_image_management/serialization_test.rb +++ b/test/unit/virtual_machine_image_management/serialization_test.rb @@ -12,24 +12,24 @@ # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- -require "test_helper" +require 'test_helper' describe Azure::VirtualMachineImageManagement::Serialization do subject { Azure::VirtualMachineImageManagement::Serialization } - let(:virtual_machine_images_from_xml) { Fixtures["list_images"] } + let(:virtual_machine_images_from_xml) { Fixtures['list_images'] } - describe "#virtual_machine_images_from_xml" do + describe '#virtual_machine_images_from_xml' do - it "accepts an XML string" do + it 'accepts an XML string' do subject.virtual_machine_images_from_xml Nokogiri::XML(virtual_machine_images_from_xml) end - it "returns an Array of VirtualMachineImageService instances" do + it 'returns an Array of VirtualMachineImageService instances' do results = subject.virtual_machine_images_from_xml Nokogiri::XML(virtual_machine_images_from_xml) results.must_be_kind_of Array results[0].must_be_kind_of Azure::VirtualMachineImageManagement::VirtualMachineImage results.count.must_equal 12 end end -end \ No newline at end of file +end diff --git a/test/unit/virtual_machine_image_management/virtual_machine_image_management_service_test.rb b/test/unit/virtual_machine_image_management/virtual_machine_image_management_service_test.rb index 07a80acae2..aa8abc9242 100644 --- a/test/unit/virtual_machine_image_management/virtual_machine_image_management_service_test.rb +++ b/test/unit/virtual_machine_image_management/virtual_machine_image_management_service_test.rb @@ -12,48 +12,48 @@ # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- -require "test_helper" +require 'test_helper' describe Azure::VirtualMachineImageManagementService do subject { Azure::VirtualMachineImageManagementService.new } - let(:request_path) {'/services/images'} - let(:images_xml) { Fixtures["list_images"] } + let(:request_path) { '/services/images' } + let(:images_xml) { Fixtures['list_images'] } let(:method) { :get } - let(:mock_request){ mock() } - let(:response) { - response = mock() + let(:mock_request) { mock } + let(:response) do + response = mock response.stubs(:body).returns(images_xml) response - } + end let(:response_body) { Nokogiri::XML response.body } - before{ + before do Loggerx.expects(:puts).returns(nil).at_least(0) - } - - describe "#list_virtual_machine_images" do - - before { + end + + describe '#list_virtual_machine_images' do + + before do ManagementHttpRequest.stubs(:new).with( method, request_path, nil ).returns(mock_request) mock_request.expects(:call).returns(response_body) - } - - it "assembles a URI for the request" do + end + + it 'assembles a URI for the request' do subject.list_virtual_machine_images end - - it "sets the properties of the virtual machine images" do + + it 'sets the properties of the virtual machine images' do virtual_machine_image = subject.list_virtual_machine_images.first virtual_machine_image.name.must_equal 'RightImage-CentOS-6.2-x64-v5.8.8.1' end - it "returns a list of virtual machine images from server" do + it 'returns a list of virtual machine images from server' do results = subject.list_virtual_machine_images results.must_be_kind_of Array results.length.must_equal 12 @@ -61,5 +61,5 @@ results.first.must_be_kind_of image_klass end end - + end diff --git a/test/unit/virtual_machine_management/serialization_test.rb b/test/unit/virtual_machine_management/serialization_test.rb index cba251c6fe..559d69424b 100644 --- a/test/unit/virtual_machine_management/serialization_test.rb +++ b/test/unit/virtual_machine_management/serialization_test.rb @@ -120,12 +120,16 @@ storage_account_name: 'storageaccountname', cloud_service_name: 'cloud-service-name', tcp_endpoints: '80,3389:3390,85:85', - availability_set_name: 'aval-set' + availability_set_name: 'aval-set', + winrm_https_port: '5988', + winrm_transport: ['http','https'] } end it 'returns an VirtualMachine object with correct tcp endpoints' do params[:certificate] = {fingerprint: 'CFB8C256D2986559C630547F2D0'} + options[:os_type] = 'Windows' + options[:existing_ports] = ['5985'] result = subject.deployment_to_xml params, options doc = Nokogiri::XML(result) endpoints = doc.css('Deployment RoleList ConfigurationSet InputEndpoints InputEndpoint') @@ -154,28 +158,37 @@ public_port: '85', local_port: '85' ) + tcp_endpoints.must_include( + name: 'PowerShell', + public_port: '5988', + local_port: '5986' + ) end end describe '#add_data_disk_to_xml' do let(:options) do - {disk_size: 100} + { + disk_size: 100, + } end let(:media_link) { 'https://sta.blob.managment.core.net/vhds/1234.vhd' } - let(:lun) { 5 } + before do Loggerx.expects(:puts).returns(nil).at_least(0) + @vm = Azure::VirtualMachineManagement::VirtualMachine.new + @vm.data_disks = [] + @vm.media_link = media_link end - it 'returns an xml for newly created data disk' do - result = subject.add_data_disk_to_xml(lun, media_link, options) + it 'returns an xml for newly created data disk' do + result = subject.add_data_disk_to_xml(@vm, options) doc = Nokogiri::XML(result) disk_size = doc.css('DataVirtualHardDisk LogicalDiskSizeInGB').text media_link = doc.css('DataVirtualHardDisk MediaLink').text disk_name = doc.css('DataVirtualHardDisk DiskName').text result.must_be_kind_of String - doc.css('DataVirtualHardDisk Lun').text.must_equal lun.to_s disk_size.must_equal options[:disk_size].to_s media_link.wont_be_empty disk_name.must_be_empty @@ -184,12 +197,11 @@ it 'returns an xml for existing data disk' do options[:import] = true options[:disk_name] = 'disk_name' - result = subject.add_data_disk_to_xml(lun, media_link, options) + result = subject.add_data_disk_to_xml(@vm, options) doc = Nokogiri::XML(result) media_link = doc.css('DataVirtualHardDisk MediaLink').text disk_name = doc.css('DataVirtualHardDisk DiskName').text result.must_be_kind_of String - doc.css('DataVirtualHardDisk Lun').text.must_equal lun.to_s media_link.must_be_empty disk_name.wont_be_empty end @@ -197,36 +209,36 @@ it 'raise error when disk name is empty' do options[:import] = true exception = assert_raises(RuntimeError) do - subject.add_data_disk_to_xml(lun, media_link, options) + subject.add_data_disk_to_xml(@vm, options) end assert_match(/The data disk name is not valid/i, exception.message) end end - describe '#add_data_disk_to_xml' do + describe '#assign_random_port' do let(:preferred_port) { '22' } before do subject.class.send(:public, *subject.class.private_instance_methods) Loggerx.expects(:puts).returns(nil).at_least(0) end - it 'returns an xml for newly created data disk' do + it 'returns random port number when preferred port is in use' do result = subject.assign_random_port(preferred_port, [preferred_port]) assert_operator result.to_i, :>=, 10000 assert_operator result.to_i, :<=, 65535 end - it 'returns an xml for newly created data disk' do + it 'returns preferred port number when used ports is nil' do result = subject.assign_random_port(preferred_port, nil) result.must_equal preferred_port end - it 'returns an xml for newly created data disk' do + it 'returns preferred port number when used ports is empty' do result = subject.assign_random_port(preferred_port, []) result.must_equal preferred_port end - it 'returns an xml for newly created data disk' do + it 'returns random port number when preferred port is in use' do result = subject.assign_random_port(preferred_port, ['1', preferred_port]) assert_operator result.to_i, :>=, 10000 assert_operator result.to_i, :<=, 65535 diff --git a/test/unit/virtual_machine_management/virtual_machine_management_service_test.rb b/test/unit/virtual_machine_management/virtual_machine_management_service_test.rb index e3eca8af63..862b958f84 100644 --- a/test/unit/virtual_machine_management/virtual_machine_management_service_test.rb +++ b/test/unit/virtual_machine_management/virtual_machine_management_service_test.rb @@ -21,11 +21,7 @@ Azure::VirtualMachineManagementService.new end - before do - Loggerx.stubs(:info).returns(nil) - end - - let(:params)do + let(:params) do { vm_name: 'instance1', vm_user: 'root', @@ -35,7 +31,7 @@ } end - let(:windows_params)do + let(:windows_params) do { vm_name: 'instance1', vm_user: 'administrator', @@ -53,56 +49,58 @@ response end let(:location_response_body) { Nokogiri::XML location_response.body } + let(:mock_virtual_machine_request) { mock } + let(:windows_images_xml) { Fixtures['list_images'] } + let(:images_request_path) { '/services/images' } + let(:mock_request) { mock } + let(:os_response_body) do + response = mock + response.stubs(:body).returns(windows_images_xml) + Nokogiri::XML response.body + end + + before do + Loggerx.stubs(:info).returns(nil) + Loggerx.expects(:puts).returns(nil).at_least(0) + ManagementHttpRequest.stubs(:new).with( + :get, + images_request_path, + nil + ).returns(mock_request) + mock_request.expects(:call).returns(os_response_body).at_least(0) + end describe '#list_virtual_machines' do let(:request_path) { '/services/hostedservices' } let(:cloud_services_xml) { Fixtures['list_cloud_services'] } let(:virtual_machine_xml) { Fixtures['virtual_machine'] } let(:deployment_error_xml) { Fixtures['deployment_error'] } - let(:virtual_networks_xml) { Fixtures['list_virtual_networks'] } - let(:method) { :get } - let(:mock_cloud_service_request) { mock } - let(:mock_virtual_machine_request) { mock } - let(:mock_virtual_network_request) { mock } - let(:cloud_service_response) do cloud_service_response = mock cloud_service_response.stubs(:body).returns(cloud_services_xml) cloud_service_response end - let(:virtual_machine_response) do virtual_machine_response = mock virtual_machine_response.stubs(:body).returns(virtual_machine_xml) virtual_machine_response end - let(:deployment_error_response) do http_error_response = mock http_error_response.stubs(:body).returns(deployment_error_xml) http_error_response end - - let(:virtual_networks_response) do - virtual_networks_response = mock - virtual_networks_response.stubs(:body).returns(virtual_networks_xml) - virtual_networks_response - end - let(:cloud_service_response_body) { Nokogiri::XML cloud_service_response.body } let(:virtual_machine_response_body) { Nokogiri::XML virtual_machine_response.body } - let(:virtual_networks_response_body) { Nokogiri::XML virtual_networks_response.body } before do ManagementHttpRequest.stubs(:new).with(method, request_path, nil).returns(mock_cloud_service_request) mock_cloud_service_request.expects(:call).returns(cloud_service_response_body) - ManagementHttpRequest.stubs(:new).with(method, '/services/hostedservices/cloud-service-1/deploymentslots/production').returns(mock_virtual_machine_request) + ManagementHttpRequest.stubs(:new).with(method, anything).returns(mock_virtual_machine_request) mock_virtual_machine_request.stubs(:warn=).returns(true).twice - ManagementHttpRequest.stubs(:new).with(method, '/services/hostedservices/cloud-service-2/deploymentslots/production').returns(mock_virtual_machine_request) mock_virtual_machine_request.expects(:call).twice.returns(virtual_machine_response_body).returns(Nokogiri::XML deployment_error_response.body) - ManagementHttpRequest.stubs(:new).with(method, '/services/networking/virtualnetwork', nil).returns(mock_virtual_network_request) end it 'assembles a URI for the request' do @@ -157,9 +155,9 @@ describe '#get_virtual_machine' do before do - virtual_machine = VirtualMachine.new do |virtual_machine| - virtual_machine.vm_name = 'instance-name' - virtual_machine.cloud_service_name = 'cloud-service-1' + virtual_machine = VirtualMachine.new do |vm| + vm.vm_name = 'instance-name' + vm.cloud_service_name = 'cloud-service-1' end Azure::VirtualMachineManagementService.any_instance.stubs( :list_virtual_machines @@ -172,38 +170,22 @@ end it 'return nil if virtual machine or cloud server does not exist ' do - virtual_machine = subject.get_virtual_machine 'name', 'cloud-service-1' - virtual_machine.must_equal nil - virtual_machine = subject.get_virtual_machine 'instance-name', 'cloud_service_name' - virtual_machine.must_equal nil + vm = subject.get_virtual_machine 'name', 'cloud-service-1' + vm.must_equal nil + vm = subject.get_virtual_machine 'name2', 'cloud_service_name' + vm.must_equal nil end it 'return virtual machine instance if virtual machine name and cloud server name are valid ' do - virtual_machine = subject.get_virtual_machine 'instance-name', 'cloud-service-1' - virtual_machine.must_be_kind_of VirtualMachine + vm = subject.get_virtual_machine 'instance-name', 'cloud-service-1' + vm.must_be_kind_of VirtualMachine end end describe '#create_virtual_machine' do - let(:images_request_path) { '/services/images' } - let(:images_xml) { Fixtures['list_images'] } - let(:virtual_machine_xml) { Fixtures['virtual_machine'] } let(:method) { :get } - let(:mock_request) { mock } - - let(:os_response_body) do - response = mock - response.stubs(:body).returns(images_xml) - Nokogiri::XML response.body - end before do - ManagementHttpRequest.stubs(:new).with( - method, - images_request_path, - nil - ).returns(mock_request) - mock_request.expects(:call).returns(os_response_body) mock_request = mock ManagementHttpRequest.stubs(:new).with( method, @@ -257,24 +239,10 @@ end describe '#create_virtual_machine with invalid parameters for windows machine' do - let(:images_request_path) { '/services/images' } - let(:windows_images_xml) { Fixtures['list_images'] } let(:virtual_machine_xml) { Fixtures['virtual_machine'] } let(:method) { :get } - let(:mock_request) { mock } - let(:os_response_body) do - response = mock - response.stubs(:body).returns(windows_images_xml) - Nokogiri::XML response.body - end before do - ManagementHttpRequest.stubs(:new).with( - method, - images_request_path, - nil - ).returns(mock_request) - mock_request.expects(:call).returns(os_response_body) mock_request = mock ManagementHttpRequest.stubs(:new).with( method, @@ -285,7 +253,6 @@ Azure::CloudServiceManagementService.any_instance.stubs(:create_cloud_service) Azure::CloudServiceManagementService.any_instance.stubs(:upload_certificate) Azure::StorageManagementService.any_instance.stubs(:create_storage_account) - Loggerx.expects(:puts).returns(nil).at_least(0) mock_request = mock ManagementHttpRequest.expects(:new).with( :post, @@ -403,19 +370,9 @@ end describe '#get_os_type' do - let(:images_xml) { Fixtures['list_images'] } - let(:mock_request) { mock } - let(:response) do - response = mock - response.stubs(:body).returns(images_xml) - response - end - let(:response_body) { Nokogiri::XML response.body } before do - ManagementHttpRequest.any_instance.expects(:call).returns response_body subject.class.send(:public, *subject.class.private_instance_methods) - Loggerx.expects(:puts).returns(nil).at_least(0) end it 'returns os type of given virtual machine image' do @@ -432,4 +389,52 @@ end end + describe '#add_role' do + + before do + windows_params[:cloud_service_name] = 'cloud-service-1' + end + + it 'should throws error when cloud service name is empty' do + windows_params.delete(:cloud_service_name) + exception = assert_raises(RuntimeError) do + subject.add_role(params) + end + error_msg = 'You did not provide a valid \'cloud_service_name\' value' + assert_match(/#{error_msg}/i, exception.message) + end + + it 'throws error when vm_user is not given' do + windows_params.delete(:vm_user) + exception = assert_raises(RuntimeError) do + subject.add_role(windows_params) + end + error_msg = 'You did not provide a valid \'vm_user\' value' + assert_match(/#{error_msg}/i, exception.message) + end + + it 'throws error when certificate path is not invalid.' do + options = { + winrm_transport: %w(https http), + private_key_file: 'f:/invalid_path/private_key' , + certificate_file: 'f:/invalid_path/certificate.pem' + } + exception = assert_raises(RuntimeError) do + subject.add_role(windows_params, options) + end + error_msg = 'No such file or directory -' + assert_match(/#{error_msg}/i, exception.message) + end + + it 'throws error when wrong role size is given' do + options = { vm_size: 'wrong size' } + exception = assert_raises(RuntimeError) do + subject.add_role(windows_params, options) + end + error_msg = "wrong size' specified for parameter 'vm_size' is invalid." + assert_match(/#{error_msg}*/, exception.message) + end + + end + end