diff --git a/lib/fog/cloudstack/models/compute/public_ip_address.rb b/lib/fog/cloudstack/models/compute/public_ip_address.rb index 19a447490..75e6a95c0 100644 --- a/lib/fog/cloudstack/models/compute/public_ip_address.rb +++ b/lib/fog/cloudstack/models/compute/public_ip_address.rb @@ -2,51 +2,94 @@ module Fog module Compute class Cloudstack class PublicIpAddress < Fog::Model - identity :id, :aliases => 'id' - attribute :network_id, :aliases => 'networkid' - attribute :associated_network_id, :aliases => 'associatednetworkid' - attribute :physical_network_id, :aliases => 'physicalnetworkid' - attribute :ip_address, :aliases => 'ipaddress' - attribute :mac_address, :aliases => 'macaddress' - attribute :state, :aliases => 'state' - attribute :traffic_type, :aliases => 'traffictype' - attribute :is_default, :aliases => 'isdefault', :type => :boolean - attribute :is_source_nat, :aliases => 'issourcenat', :type => :boolean - attribute :is_static_nat, :aliases => 'isstaticnat', :type => :boolean - attribute :is_system, :aliases => 'issytem', :type => :boolean - attribute :is_portable, :aliases => 'isportable', :type => :boolean - attribute :allocated, :aliases => 'allocated', :type => :time - attribute :zone_id, :aliases => 'zone_id' - attribute :domain_id, :aliases => 'domain_id' - attribute :tags, :type => :array - attribute :type + identity :id - def save - requires :display_text, :name + attribute :account + attribute :allocated, :type => :time + attribute :associated_network_id, :aliases => 'associatednetworkid' + attribute :associated_network_name, :aliases => 'associatednetworkname' + attribute :domain + attribute :domain_id, :aliases => 'domainid' + attribute :for_virtual_network, :type => :boolean, :aliases => 'forvirtualnetwork' + attribute :ip_address, :aliases => 'ipaddress' + attribute :is_portable, :type => :boolean, :aliases => 'isportable' + attribute :is_source_nat, :type => :boolean, :aliases => 'issourcenat' + attribute :is_static_nat, :type => :boolean, :aliases => 'isstaticnat' + attribute :is_system, :type => :boolean, :aliases => 'issystem' + attribute :network_id, :type => :boolean, :aliases => 'networkid' + attribute :physical_network_id, :aliases => 'physicalnetworkid' + attribute :project + attribute :project_id, :aliases => 'projectid' + attribute :purpose + attribute :state + attribute :server_display_name, :aliases => 'virtualmachinedisplayname' + attribute :server_id, :aliases => 'virtualmachineid' + attribute :server_name, :aliases => 'virtualmachinename' + attribute :vlan_id, :aliases => 'vlanid' + attribute :vlan_name, :aliases => 'vlanname' + attribute :server_ip_address, :aliases => 'vmipaddress' + attribute :vpc_id, :aliases => 'vpcid' + attribute :zone_id, :aliases => 'zoneid' + attribute :zone_name, :aliases => 'zonename' + attribute :tags, :type => :array + attribute :job_id, :aliases => 'jobid' # only on associate - options = { - 'displaytext' => display_text, - 'name' => name, - 'customized' => is_customized, - 'disksize' => disk_size, - 'domain_id' => domain_id, - 'storagetype' => storage_type, - 'tags' => tags - } + def initialize(attributes = {}) + # assign server first to prevent race condition with persisted? + self.server = attributes.delete(:server) + super + end - response = service.associate_ip_address(options) - merge_attributes(response['associateipaddressresponse']) + def ready? + state == 'Allocated' end def destroy - requires :id - - response = service.disassociate_ip_address('id' => id ) - success_status = response['disassociateipaddressresponse']['success'] - - success_status == 'true' + requires :identity + service.disassociate_ip_address('id' => id) + true end + def server=(new_server) + @server = new_server + if persisted? + if !server_id.nil? && (new_server.nil? || server_id != new_server.id) + service.disable_static_nat('ipaddressid' => id) + self.server_display_name = nil + self.server_id = nil + self.server_name = nil + self.server_ip_address = nil + self.is_static_nat = false + end + unless new_server.nil? + service.enable_static_nat( + 'ipaddressid' => id, 'virtualmachineid' => new_server.id) + end + end + end + + def server + service.servers.get(server_id) + end + + def save + raise Fog::Errors::Error.new('Resaving an existing object may create a duplicate') if persisted? + options = { + 'account' => account, + 'domainid' => domain_id, + 'isportable' => is_portable, + 'networkid' => network_id, + 'projectid' => project_id, + 'vpcid' => vpc_id, + 'zoneid' => zone_id, + } + response = service.associate_ip_address(options) + merge_attributes(response['associateipaddressresponse']) + if @server + self.server = @server + end + true + end end end end diff --git a/lib/fog/cloudstack/models/compute/public_ip_addresses.rb b/lib/fog/cloudstack/models/compute/public_ip_addresses.rb index 9781f5ad5..4e4cc3ca2 100644 --- a/lib/fog/cloudstack/models/compute/public_ip_addresses.rb +++ b/lib/fog/cloudstack/models/compute/public_ip_addresses.rb @@ -1,5 +1,5 @@ require 'fog/core/collection' -require 'fog/cloudstack/models/compute/address' +require 'fog/cloudstack/models/compute/public_ip_address' module Fog module Compute @@ -14,10 +14,12 @@ module Fog end def get(address_id) - options = { 'id' => address_id } - response = service.list_public_ip_addresses(options) - public_ip_addresses = response["listpublicipaddressesresponse"]["publicipaddress"].first - new(public_ip_addresses) + response = service.list_public_ip_addresses('id' => address_id) + if public_ip_address = response["listpublicipaddressesresponse"]["publicipaddress"].first + new(public_ip_address) + end + rescue Fog::Compute::Cloudstack::BadRequest + nil end end end diff --git a/lib/fog/cloudstack/requests/compute/associate_ip_address.rb b/lib/fog/cloudstack/requests/compute/associate_ip_address.rb index 447f97db7..42fa5141b 100644 --- a/lib/fog/cloudstack/requests/compute/associate_ip_address.rb +++ b/lib/fog/cloudstack/requests/compute/associate_ip_address.rb @@ -22,8 +22,9 @@ module Fog class Mock def associate_ip_address(*args) + public_ip_address_id = Fog::Cloudstack.uuid public_ip_address = { - "id" => "f2f2f2f2-f2f2-f2f2-f2f2-f2f2f2f2f2", + "id" => public_ip_address_id, "ipaddress" => "192.168.200.3", "allocated" => "2014-12-22T22:32:39+0000", "zoneid" => "0e276270-7950-4483-bf21-3dc897dbe08a", @@ -48,7 +49,6 @@ module Fog {'associateipaddressresponse' => public_ip_address} end end - end end end diff --git a/lib/fog/cloudstack/requests/compute/disable_static_nat.rb b/lib/fog/cloudstack/requests/compute/disable_static_nat.rb index 4a02d7ecd..d160c7753 100644 --- a/lib/fog/cloudstack/requests/compute/disable_static_nat.rb +++ b/lib/fog/cloudstack/requests/compute/disable_static_nat.rb @@ -19,6 +19,32 @@ module Fog end end + class Mock + def disable_static_nat(*args) + ip_address_id = args[0].is_a?(Hash) ? args[0]['ipaddressid'] : args[0] + + address = self.data[:public_ip_addresses][ip_address_id] + + unless address + raise Fog::Compute::Cloudstack::BadRequest.new( +"Unable to execute API command disablestaticnat due to invalid value. \ +Invalid parameter ipaddressid value=#{ip_address_id} due to incorrect long value format, \ +or entity does not exist or due to incorrect parameter annotation for the field in api cmd class.") + end + + if address['virtualmachineid'].nil? + raise Fog::Compute::Cloudstack::BadRequest.new( +"Specified IP address id is not associated with any vm Id") + end + + address.merge!( + 'virtualmachineid' => nil, + 'virtualmachinname' => nil, + 'virtualmachinedisplayname' => nil + ) + {'enablestaticnatresponse' => {'success' => 'true'}} + end + end end end end diff --git a/lib/fog/cloudstack/requests/compute/disassociate_ip_address.rb b/lib/fog/cloudstack/requests/compute/disassociate_ip_address.rb index 96244f3bf..1b2855909 100644 --- a/lib/fog/cloudstack/requests/compute/disassociate_ip_address.rb +++ b/lib/fog/cloudstack/requests/compute/disassociate_ip_address.rb @@ -21,7 +21,7 @@ module Fog class Mock def disassociate_ip_address(*args) - public_ip_address_id = options['id'] + public_ip_address_id = args[0].is_a?(Hash) ? args[0]['id'] : args[0] if self.data[:public_ip_addresses][public_ip_address_id] self.data[:public_ip_addresses].delete(public_ip_address_id) { "disassociateipaddressresponse" => { "success" => "true" }} diff --git a/lib/fog/cloudstack/requests/compute/enable_static_nat.rb b/lib/fog/cloudstack/requests/compute/enable_static_nat.rb index 44769594b..6a014952b 100644 --- a/lib/fog/cloudstack/requests/compute/enable_static_nat.rb +++ b/lib/fog/cloudstack/requests/compute/enable_static_nat.rb @@ -20,6 +20,49 @@ module Fog end end + class Mock + def enable_static_nat(*args) + ip_address_id = nil + virtual_machine_id = nil + if args[0].is_a? Hash + ip_address_id = args[0]['ipaddressid'] + virtual_machine_id = args[0]['virtualmachineid'] + else + ip_address_id = args[0] + virtual_machine_id = args[1] + end + + server = self.data[:servers][virtual_machine_id] + address = self.data[:public_ip_addresses][ip_address_id] + + unless server + raise Fog::Compute::Cloudstack::BadRequest.new( +"Unable to execute API command enablestaticnat due to invalid value. \ +Invalid parameter virtualmachineid value=#{virtual_machine_id} due to incorrect long value format, \ +or entity does not exist or due to incorrect parameter annotation for the field in api cmd class.") + end + + unless address + raise Fog::Compute::Cloudstack::BadRequest.new( +"Unable to execute API command enablestaticnat due to invalid value. \ +Invalid parameter ipaddressid value=#{ip_address_id} due to incorrect long value format, \ +or entity does not exist or due to incorrect parameter annotation for the field in api cmd class.") + end + + unless address['virtualmachineid'].nil? + raise Fog::Compute::Cloudstack::BadRequest.new( +"Failed to enable static nat for the ip address id=#{ip_address_id} \ +as vm id=#{virtual_machine_id} is already associated with ip id=#{ip_address_id}") + end + + address.merge!( + 'virtualmachineid' => server['id'], + 'virtualmachinname' => server['name'], + 'virtualmachinedisplayname' => server['displayname'] + ) + {'enablestaticnatresponse' => {'success' => 'true'}} + end + end end end end diff --git a/lib/fog/cloudstack/requests/compute/list_public_ip_addresses.rb b/lib/fog/cloudstack/requests/compute/list_public_ip_addresses.rb index 32fe07a85..cfd55e3ca 100644 --- a/lib/fog/cloudstack/requests/compute/list_public_ip_addresses.rb +++ b/lib/fog/cloudstack/requests/compute/list_public_ip_addresses.rb @@ -23,12 +23,22 @@ module Fog end class Mock - def list_public_ip_addresses(*arg) - public_ip_addresses = self.data[:public_ip_addresses] - { "listpublicipaddressesresponse" => { "count"=> public_ip_addresses.count, "publicipaddress"=> public_ip_addresses.values } } + def list_public_ip_addresses(*args) + public_ip_address_id = args[0].is_a?(Hash) ? args[0]['id'] : nil + if public_ip_address_id + public_ip_addresses = [self.data[:public_ip_addresses][public_ip_address_id]] + else + public_ip_addresses = self.data[:public_ip_addresses].values + end + + { + 'listpublicipaddressesresponse' => { + 'count' => public_ip_addresses.size, + 'publicipaddress' => public_ip_addresses + } + } end end - end end end diff --git a/tests/cloudstack/compute/models/public_ip_address_tests.rb b/tests/cloudstack/compute/models/public_ip_address_tests.rb new file mode 100644 index 000000000..c073f4cde --- /dev/null +++ b/tests/cloudstack/compute/models/public_ip_address_tests.rb @@ -0,0 +1,27 @@ +Shindo.tests("Fog::Compute[:cloudstack] | public_ip_address", "cloudstack") do + config = compute_providers[:cloudstack] + compute = Fog::Compute[:cloudstack] + + model_tests(compute.public_ip_addresses, config[:public_ip_address_attributes], config[:mocked]) do + @server = Fog::Compute[:cloudstack].servers.create(config[:server_attributes]) + @server.wait_for { ready? } + + tests('#server=').succeeds do + @instance.server = @server + end + + tests('#server') do + test(' == @server') do + @instance.reload + @instance.server_id == @server.id + end + end + + test('#server = nil') do + @instance.server = nil + @instance.server_id.nil? + end + + @server.destroy + end +end diff --git a/tests/cloudstack/compute/models/public_ip_addresses_tests.rb b/tests/cloudstack/compute/models/public_ip_addresses_tests.rb new file mode 100644 index 000000000..da8405fda --- /dev/null +++ b/tests/cloudstack/compute/models/public_ip_addresses_tests.rb @@ -0,0 +1,5 @@ +Shindo.tests("Fog::Compute[:cloudstack] | public_ip_addresses", ['cloudstack']) do + + collection_tests(Fog::Compute[:cloudstack].public_ip_addresses, {}, true) + +end diff --git a/tests/compute/helper.rb b/tests/compute/helper.rb index c71f85671..d51c5946b 100644 --- a/tests/compute/helper.rb +++ b/tests/compute/helper.rb @@ -56,6 +56,14 @@ def compute_providers }, :disk_offering_attributes => { :name => "new disk offering", :display_text => 'New Disk Offering' }, :egress_firewall_rule_attributes => { :protocol => "tcp", :network_id => "8aacae29-e0a4-4b7b-8a7a-3ee11cfb4362", :cidr_list =>"10.1.1.0/24"}, + :public_ip_address_attributes => {}.tap do |hash| + [:zone_id].each do |k| + key = "cloudstack_#{k}".to_sym + if Fog.credentials[key] + hash[k]= Fog.credentials[key] + end + end + end, :mocked => true }, :glesys => {