diff --git a/lib/fog/aws.rb b/lib/fog/aws.rb index f069b64c9..d62e7ba5e 100644 --- a/lib/fog/aws.rb +++ b/lib/fog/aws.rb @@ -234,6 +234,10 @@ module Fog "sg-#{Fog::Mock.random_hex(8)}" end + def self.network_interface_id + "eni-#{Fog::Mock.random_hex(8)}" + end + def self.key_id(length=21) #Probably close enough Fog::Mock.random_selection('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',length) diff --git a/lib/fog/aws/compute.rb b/lib/fog/aws/compute.rb index bc119c665..517eb7b81 100644 --- a/lib/fog/aws/compute.rb +++ b/lib/fog/aws/compute.rb @@ -17,6 +17,8 @@ module Fog collection :images model :key_pair collection :key_pairs + model :network_interface + collection :network_interfaces model :security_group collection :security_groups model :server @@ -37,11 +39,13 @@ module Fog request_path 'fog/aws/requests/compute' request :allocate_address request :associate_address + request :attach_network_interface request :attach_volume request :authorize_security_group_ingress request :cancel_spot_instance_requests request :create_image request :create_key_pair + request :create_network_interface request :create_placement_group request :create_security_group request :create_snapshot @@ -51,6 +55,7 @@ module Fog request :create_volume request :create_vpc request :delete_key_pair + request :delete_network_interface request :delete_security_group request :delete_placement_group request :delete_snapshot @@ -67,6 +72,8 @@ module Fog request :describe_reserved_instances request :describe_instance_status request :describe_key_pairs + request :describe_network_interface_attribute + request :describe_network_interfaces request :describe_placement_groups request :describe_regions request :describe_reserved_instances_offerings @@ -80,6 +87,7 @@ module Fog request :describe_volumes request :describe_volume_status request :describe_vpcs + request :detach_network_interface request :detach_volume request :disassociate_address request :get_console_output @@ -87,12 +95,14 @@ module Fog request :import_key_pair request :modify_image_attribute request :modify_instance_attribute + request :modify_network_interface_attribute request :modify_snapshot_attribute request :purchase_reserved_instances_offering request :reboot_instances request :release_address request :register_image request :request_spot_instances + request :reset_network_interface_attribute request :revoke_security_group_ingress request :run_instances request :terminate_instances @@ -164,6 +174,7 @@ module Fog 'ownerId' => owner_id } }, + :network_interfaces => {}, :snapshots => {}, :volumes => {}, :tags => {}, diff --git a/lib/fog/aws/models/compute/network_interface.rb b/lib/fog/aws/models/compute/network_interface.rb new file mode 100644 index 000000000..93a79800c --- /dev/null +++ b/lib/fog/aws/models/compute/network_interface.rb @@ -0,0 +1,70 @@ +require 'fog/core/model' + +module Fog + module Compute + class AWS + + class NetworkInterface < Fog::Model + + identity :network_interface_id, :aliases => 'networkInterfaceId' + attribute :state + attribute :request_id, :aliases => 'requestId' + attribute :network_interface_id, :aliases => 'networkInterfaceId' + attribute :subnet_id, :aliases => 'subnetId' + attribute :vpc_id, :aliases => 'vpcId' + attribute :availability_zone, :aliases => 'availabilityZone' + attribute :description, :aliases => 'description' + attribute :owner_id, :aliases => 'ownerId' + attribute :requester_id, :aliases => 'requesterId' + attribute :requester_managed, :aliases => 'requesterManaged' + attribute :status, :aliases => 'status' + attribute :mac_address, :aliases => 'macAddress' + attribute :private_ip_address, :aliases => 'privateIpAddress' + attribute :private_dns_name, :aliases => 'privateDnsName' + attribute :source_dest_check, :aliases => 'sourceDestCheck' + attribute :group_set, :aliases => 'groupSet' + attribute :attachment, :aliases => 'attachment' + attribute :association, :aliases => 'association' + attribute :tag_set, :aliases => 'tagSet' + + + # Removes an existing network interface + # + # network_interface.destroy + # + # ==== Returns + # + # True or false depending on the result + # + + def destroy + requires :network_interface_id + + connection.delete_network_interface(network_interface_id) + true + end + + # Create a network_interface + # + # >> g = AWS.network_interfaces.new(:subnet_id => "subnet-someId", options) + # >> g.save + # + # options is an optional hash which may contain 'PrivateIpAddress', 'Description', 'groupSet' + # + # == Returns: + # + # requestId and a networkInterface object + # + + def save + requires :subnet_id + data = connection.create_network_interface(subnet_id).body['networkInterface'] + new_attributes = data.reject {|key,value| key == 'requestId'} + merge_attributes(new_attributes) + true + end + + end + end + end +end diff --git a/lib/fog/aws/models/compute/network_interfaces.rb b/lib/fog/aws/models/compute/network_interfaces.rb new file mode 100644 index 000000000..f76934155 --- /dev/null +++ b/lib/fog/aws/models/compute/network_interfaces.rb @@ -0,0 +1,136 @@ +require 'fog/core/collection' +require 'fog/aws/models/compute/network_interface' + +module Fog + module Compute + class AWS + + class NetworkInterfaces < Fog::Collection + + attribute :filters + + model Fog::Compute::AWS::NetworkInterface + + # Creates a new network interface + # + # AWS.network_interfaces.new + # + # ==== Returns + # + # Returns the details of the new network interface + # + #>> AWS.network_interfaces.new + # + # + + def initialize(attributes) + self.filters ||= {} + super + end + + # Returns an array of all network interfaces that have been created + # + # AWS.network_interfaces.all + # + # ==== Returns + # + # Returns an array of all network interfaces + # + #>> AWS.network_interfaves.all + # + # ] + # > + # + + def all(filters = filters) + self.filters = filters + data = connection.describe_network_interfaces(filters).body + load(data['networkInterfaceSet']) + end + + # Used to retrieve a network interface + # network interface id is required to get any information + # + # You can run the following command to get the details: + # AWS.network_interfaces.get("eni-11223344") + # + # ==== Returns + # + #>> AWS.NetworkInterface.get("eni-11223344") + # + # + + def get(nic_id) + if nic_id + self.class.new(:connection => connection).all('network-interface-id' => nic_id).first + end + end + end + + end + end +end diff --git a/lib/fog/aws/parsers/compute/attach_network_interface.rb b/lib/fog/aws/parsers/compute/attach_network_interface.rb new file mode 100644 index 000000000..11cfce0bf --- /dev/null +++ b/lib/fog/aws/parsers/compute/attach_network_interface.rb @@ -0,0 +1,18 @@ +module Fog + module Parsers + module Compute + module AWS + + class AttachNetworkInterface < Fog::Parsers::Base + + def end_element(name) + case name + when 'requestId', 'attachmentId' + @response[name] = value + end + end + end + end + end + end +end diff --git a/lib/fog/aws/parsers/compute/create_network_interface.rb b/lib/fog/aws/parsers/compute/create_network_interface.rb new file mode 100644 index 000000000..d965b7630 --- /dev/null +++ b/lib/fog/aws/parsers/compute/create_network_interface.rb @@ -0,0 +1,28 @@ +module Fog + module Parsers + module Compute + module AWS + require 'fog/aws/parsers/compute/network_interface_parser' + + class CreateNetworkInterface < NetworkInterfaceParser + def reset + super + @response = { 'networkInterface' => {} } + end + + def end_element(name) + case name + when 'requestId' + @response[name] = value + when 'networkInterface' + @response['networkInterface'] = @nic + reset_nic + else + super + end + end + end + end + end + end +end diff --git a/lib/fog/aws/parsers/compute/delete_network_interface.rb b/lib/fog/aws/parsers/compute/delete_network_interface.rb new file mode 100644 index 000000000..ff2586c06 --- /dev/null +++ b/lib/fog/aws/parsers/compute/delete_network_interface.rb @@ -0,0 +1,24 @@ +module Fog + module Parsers + module Compute + module AWS + + class DeleteNetworkInterface < Fog::Parsers::Base + + def end_element(name) + case name + when 'requestId' + @response[name] = value + when 'return' + if value == 'true' + @response[name] = true + else + @response[name] = false + end + end + end + end + end + end + end +end diff --git a/lib/fog/aws/parsers/compute/describe_network_interface_attribute.rb b/lib/fog/aws/parsers/compute/describe_network_interface_attribute.rb new file mode 100644 index 000000000..bbb011649 --- /dev/null +++ b/lib/fog/aws/parsers/compute/describe_network_interface_attribute.rb @@ -0,0 +1,79 @@ +module Fog + module Parsers + module Compute + module AWS + + class DescribeNetworkInterfaceAttribute < NetworkInterfaceParser + def reset + @response = { } + @in_description = false + @in_group_set = false + @in_source_dest_check = false + @in_attachment = false + end + + def start_element(name, attrs = []) + super + case name + when 'description' + @in_description = true + when 'groupSet' + @in_group_set = true + @group = {} + unless @response.key?('groupSet') + @response['groupSet'] = {} + end + when 'sourceDestCheck' + @in_source_dest_check = true + when 'attachment' + @in_attachment = true + @attachment = {} + end + end + + def end_element(name) + if @in_description + case name + when 'value' + @response['description'] = value + when 'description' + @in_description = false + end + elsif @in_group_set + case name + when 'item' + @response['groupSet'][@group['groupId']] = @group['groupName'] + @group = {} + when 'groupId', 'groupName' + @group[name] = value + when 'groupSet' + @in_group_set = false + end + elsif @in_source_dest_check + case name + when 'value' + @response['sourceDestCheck'] = (value == 'true') + when 'sourceDestCheck' + @in_source_dest_check = false + end + elsif @in_attachment + case name + when 'attachmentId', 'instanceId', 'instanceOwnerId', 'deviceIndex', 'status', 'attachTime', 'deleteOnTermination' + @attachment[name] = value + when 'attachment' + @response['attachment'] = @attachment + @in_attachment = false + end + else + case name + when 'requestId', 'networkInterfaceId' + @response[name] = value + end + end + end + end + end + end + end +end + diff --git a/lib/fog/aws/parsers/compute/describe_network_interfaces.rb b/lib/fog/aws/parsers/compute/describe_network_interfaces.rb new file mode 100644 index 000000000..09c519cc6 --- /dev/null +++ b/lib/fog/aws/parsers/compute/describe_network_interfaces.rb @@ -0,0 +1,43 @@ +module Fog + module Parsers + module Compute + module AWS + require 'fog/aws/parsers/compute/network_interface_parser' + + class DescribeNetworkInterfaces < NetworkInterfaceParser + def reset + super + @response = { 'networkInterfaceSet' => [] } + @item_level = 0 + end + + def start_element(name, attrs = []) + super + case name + when 'item' + @item_level += 1 + end + end + + def end_element(name) + case name + when 'requestId' + @response[name] = value + when 'item' + @item_level -= 1 + if @item_level == 0 + @response['networkInterfaceSet'] << @nic + reset_nic + else + super + end + else + super + end + end + end + end + end + end +end + diff --git a/lib/fog/aws/parsers/compute/detach_network_interface.rb b/lib/fog/aws/parsers/compute/detach_network_interface.rb new file mode 100644 index 000000000..d65685594 --- /dev/null +++ b/lib/fog/aws/parsers/compute/detach_network_interface.rb @@ -0,0 +1,24 @@ +module Fog + module Parsers + module Compute + module AWS + + class DetachNetworkInterface < Fog::Parsers::Base + + def end_element(name) + case name + when 'requestId' + @response[name] = value + when 'return' + if value == 'true' + @response[name] = true + else + @response[name] = false + end + end + end + end + end + end + end +end diff --git a/lib/fog/aws/parsers/compute/network_interface_parser.rb b/lib/fog/aws/parsers/compute/network_interface_parser.rb new file mode 100644 index 000000000..6f8f58f0b --- /dev/null +++ b/lib/fog/aws/parsers/compute/network_interface_parser.rb @@ -0,0 +1,89 @@ +module Fog + module Parsers + module Compute + module AWS + + class NetworkInterfaceParser < Fog::Parsers::Base + + def reset_nic + @nic = { 'groupSet' => {}, 'attachment' => {}, 'association' => {}, 'tagSet' => {} } + @in_tag_set = false + @in_group_set = false + @in_attachment = false + @in_association = false + end + + def reset + reset_nic + end + + def start_element(name, attrs = []) + super + case name + when 'tagSet' + @in_tag_set = true + @tag = {} + when 'groupSet' + @in_group_set = true + @group = {} + when 'attachment' + @in_attachment = true + @attachment = {} + when 'association' + @in_association = true + @association = {} + end + end + + def end_element(name) + if @in_tag_set + case name + when 'item' + @nic['tagSet'][@tag['key']] = @tag['value'] + @tag = {} + when 'key', 'value' + @tag[name] = value + when 'tagSet' + @in_tag_set = false + end + elsif @in_group_set + case name + when 'item' + @nic['groupSet'][@group['groupId']] = @group['groupName'] + @group = {} + when 'groupId', 'groupName' + @group[name] = value + when 'groupSet' + @in_group_set = false + end + elsif @in_attachment + case name + when 'attachmentId', 'instanceId', 'instanceOwnerId', 'deviceIndex', 'status', 'attachTime', 'deleteOnTermination' + @attachment[name] = value + when 'attachment' + @nic['attachment'] = @attachment + @in_attachment = false + end + elsif @in_association + case name + when 'associationId', 'publicIp', 'ipOwnerId' + @association[name] = value + when 'association' + @nic['association'] = @association + @in_association = false + end + else + case name + when 'networkInterfaceId', 'subnetId', 'vpcId', 'availabilityZone', 'description', 'ownerId', 'requesterId', + 'requesterManaged', 'status', 'macAddress', 'privateIpAddress', 'privateDnsName' + @nic[name] = value + when 'sourceDestCheck' + @nic['sourceDestCheck'] = (value == 'true') + end + end + end + end + end + end + end +end diff --git a/lib/fog/aws/requests/compute/attach_network_interface.rb b/lib/fog/aws/requests/compute/attach_network_interface.rb new file mode 100644 index 000000000..f613b1b55 --- /dev/null +++ b/lib/fog/aws/requests/compute/attach_network_interface.rb @@ -0,0 +1,57 @@ +module Fog + module Compute + class AWS + class Real + + require 'fog/aws/parsers/compute/attach_network_interface' + + # Attach a network interface + # + # ==== Parameters + # * networkInterfaceId<~String> - ID of the network interface to attach + # * instanceId<~String> - ID of the instance that will be attached to the network interface + # * deviceIndex<~Integer> - index of the device for the network interface attachment on the instance + # + # === Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * 'requestId'<~String> - Id of request + # * 'attachmentId'<~String> - ID of the attachment + # + # {Amazon API Reference}[http://docs.amazonwebservices.com/AWSEC2/2012-03-01/APIReference/index.html?ApiReference-query-AttachNetworkInterface.html] + def attach_network_interface(nic_id, instance_id, device_index) + request( + 'Action' => 'AttachNetworkInterface', + 'NetworkInterfaceId' => nic_id, + 'InstanceId' => instance_id, + 'DeviceIndex' => device_index, + :parser => Fog::Parsers::Compute::AWS::AttachNetworkInterface.new + ) + end + end + + + class Mock + + def attach_network_interface(nic_id, instance_id, device_index) + response = Excon::Response.new + if self.data[:network_interfaces][nic_id] + attachment = self.data[:network_interfaces][nic_id]['attachment'] + attachment['attachmentId'] = Fog::AWS::Mock.request_id + attachment['instanceId'] = instance_id + + response.status = 200 + response.body = { + 'requestId' => Fog::AWS::Mock.request_id, + 'attachmentId' => attachment['attachmentId'] + } + else + raise Fog::Compute::AWS::NotFound.new("The network interface '#{nic_id}' does not exist") + end + + response + end + end + end + end +end diff --git a/lib/fog/aws/requests/compute/create_network_interface.rb b/lib/fog/aws/requests/compute/create_network_interface.rb new file mode 100644 index 000000000..0b283ec92 --- /dev/null +++ b/lib/fog/aws/requests/compute/create_network_interface.rb @@ -0,0 +1,118 @@ +module Fog + module Compute + class AWS + class Real + + require 'fog/aws/parsers/compute/create_network_interface' + + # Creates a network interface + # + # ==== Parameters + # * subnetId<~String> - The ID of the subnet to associate with the network interface + # * options<~Hash>: + # * PrivateIpAddress<~String> - The private IP address of the network interface + # * Description<~String> - The description of the network interface + # * groupSet<~Array> - The security group IDs for use by the network interface + # + # === Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * 'requestId'<~String> - Id of request + # * 'networkInterface'<~Hash> - The created network interface + # * 'networkInterfaceId'<~String> - The ID of the network interface + # * 'subnetId'<~String> - The ID of the subnet + # * 'vpcId'<~String> - The ID of the VPC + # * 'availabilityZone'<~String> - The availability zone + # * 'description'<~String> - The description + # * 'ownerId'<~String> - The ID of the person who created the interface + # * 'requesterId'<~String> - The ID ot teh entity requesting this interface + # * 'requesterManaged'<~String> - + # * 'status'<~String> - "available" or "in-use" + # * 'macAddress'<~String> - + # * 'privateIpAddress'<~String> - IP address of the interface within the subnet + # * 'privateDnsName'<~String> - The private DNS name + # * 'sourceDestCheck'<~Boolean> - Flag indicating whether traffic to or from the instance is validated + # * 'groupSet'<~Hash> - Associated security groups + # * 'key'<~String> - ID of associated group + # * 'value'<~String> - Name of associated group + # * 'attachment'<~Hash>: - Describes the way this nic is attached + # * 'attachmentID'<~String> + # * 'instanceID'<~String> + # * 'association'<~Hash>: - Describes an eventual instance association + # * 'attachmentID'<~String> - ID of the network interface attachment + # * 'instanceID'<~String> - ID of the instance attached to the network interface + # * 'publicIp'<~String> - Address of the Elastic IP address bound to the network interface + # * 'ipOwnerId'<~String> - ID of the Elastic IP address owner + # * 'tagSet'<~Array>: - Tags assigned to the resource. + # * 'key'<~String> - Tag's key + # * 'value'<~String> - Tag's value + # + # {Amazon API Reference}[http://docs.amazonwebservices.com/AWSEC2/2012-03-01/APIReference/ApiReference-query-CreateNetworkInterface.html] + def create_network_interface(subnetId, options = {}) + if security_groups = options.delete('GroupSet') + options.merge!(Fog::AWS.indexed_param('SecurityGroupId', [*security_groups])) + end + request({ + 'Action' => 'CreateNetworkInterface', + 'SubnetId' => subnetId, + :parser => Fog::Parsers::Compute::AWS::CreateNetworkInterface.new + }.merge!(options)) + + end + end + + class Mock + def create_network_interface(subnetId, options = {}) + response = Excon::Response.new + if subnetId + id = Fog::AWS::Mock.network_interface_id + + groups = {} + if options['GroupSet'] + options['GroupSet'].each do |group_id| + name = self.data[:security_groups].select { |k,v| v['groupId'] == group_id } .first.first + if name.nil? + raise Fog::Compute::AWS::Error.new("Unknown security group '#{group_id}' specified") + end + groups[group_id] = name + end + end + if options['PrivateIpAddress'].nil? + options['PrivateIpAddress'] = "10.0.0.2" + end + + data = { + 'networkInterfaceId' => id, + 'subnetId' => subnetId, + 'vpcId' => 'mock-vpc-id', + 'availabilityZone' => 'mock-zone', + 'description' => options['Description'], + 'ownerId' => '', + 'requesterManaged' => 'false', + 'status' => 'available', + 'macAddress' => '00:11:22:33:44:55', + 'privateIpAddress' => options['PrivateIpAddress'], + 'sourceDestCheck' => true, + 'groupSet' => groups, + 'attachment' => {}, + 'association' => {}, + 'tagSet' => {} + } + self.data[:network_interfaces][id] = data + response.body = { + 'requestId' => Fog::AWS::Mock.request_id, + 'networkInterface' => data + } + response + else + response.status = 400 + response.body = { + 'Code' => 'InvalidParameterValue', + 'Message' => "Invalid value '' for subnetId" + } + end + end + end + end + end +end diff --git a/lib/fog/aws/requests/compute/delete_network_interface.rb b/lib/fog/aws/requests/compute/delete_network_interface.rb new file mode 100644 index 000000000..adc5a3ef9 --- /dev/null +++ b/lib/fog/aws/requests/compute/delete_network_interface.rb @@ -0,0 +1,52 @@ +module Fog + module Compute + class AWS + class Real + + require 'fog/aws/parsers/compute/delete_network_interface' + # Deletes a network interface. + # + # ==== Parameters + # * network_interface_id<~String> - The ID of the network interface you want to delete. + # + # === Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * 'requestId'<~String> - Id of request + # * 'return'<~Boolean> - Returns true if the request succeeds. + # + # {Amazon API Reference}[http://docs.amazonwebservices.com/AWSEC2/2012-03-01/APIReference/ApiReference-query-DeleteNetworkInterface.html] + def delete_network_interface(network_interface_id) + request( + 'Action' => 'DeleteNetworkInterface', + 'NetworkInterfaceId' => network_interface_id, + :parser => Fog::Parsers::Compute::AWS::DeleteNetworkInterface.new + ) + end + end + + class Mock + def delete_network_interface(network_interface_id) + response = Excon::Response.new + if self.data[:network_interfaces][network_interface_id] + + if self.data[:network_interfaces][network_interface_id]['attachment']['attachmentId'] + raise Fog::Compute::AWS::Error.new("Interface is in use") + end + + self.data[:network_interfaces].delete(network_interface_id) + + response.status = 200 + response.body = { + 'requestId' => Fog::AWS::Mock.request_id, + 'return' => true + } + response + else + raise Fog::Compute::AWS::NotFound.new("The network interface '#{network_interface_id}' does not exist") + end + end + end + end + end +end diff --git a/lib/fog/aws/requests/compute/describe_network_interface_attribute.rb b/lib/fog/aws/requests/compute/describe_network_interface_attribute.rb new file mode 100644 index 000000000..32abcb260 --- /dev/null +++ b/lib/fog/aws/requests/compute/describe_network_interface_attribute.rb @@ -0,0 +1,67 @@ +module Fog + module Compute + class AWS + class Real + + require 'fog/aws/parsers/compute/describe_network_interface_attribute' + # Describes a network interface attribute value + # + # ==== Parameters + # * network_interface_id<~String> - The ID of the network interface you want to describe an attribute of + # * attribute<~String> - The attribute to describe, must be one of 'description', 'groupSet', 'sourceDestCheck' or 'attachment' + # + # === Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * 'requestId'<~String> - Id of request + # * 'networkInterfaceId'<~String> - The ID of the network interface + # * 'description'<~String> - The description (if requested) + # * 'groupSet'<~Hash> - Associated security groups (if requested) + # * 'key'<~String> - ID of associated group + # * 'value'<~String> - Name of associated group + # * 'sourceDestCheck'<~Boolean> - Flag indicating whether traffic to or from the instance is validated (if requested) + # * 'attachment'<~Hash>: - Describes the way this nic is attached (if requested) + # * 'attachmentID'<~String> + # * 'instanceID'<~String> + # * 'instanceOwnerId'<~String> + # * 'deviceIndex'<~Integer> + # * 'status'<~String> + # * 'attachTime'<~String> + # * 'deleteOnTermination<~Boolean> + # + # {Amazon API Reference}[http://docs.amazonwebservices.com/AWSEC2/2012-03-01/APIReference/ApiReference-query-DescribeNetworkInterfaceAttribute.html] + def describe_network_interface_attribute(network_interface_id, attribute) + request( + 'Action' => 'DescribeNetworkInterfaceAttribute', + 'NetworkInterfaceId' => network_interface_id, + 'Attribute' => attribute, + :parser => Fog::Parsers::Compute::AWS::DescribeNetworkInterfaceAttribute.new + ) + end + end + + class Mock + def describe_network_interface_attribute(network_interface_id, attribute) + response = Excon::Response.new + if self.data[:network_interfaces][network_interface_id] + + response.status = 200 + response.body = { + 'requestId' => Fog::AWS::Mock.request_id, + 'networkInterfaceId' => network_interface_id + } + case attribute + when 'description', 'groupSet', 'sourceDestCheck', 'attachment' + response.body[attribute] = self.data[:network_interfaces][network_interface_id][attribute] + else + raise Fog::Compute::AWS::Error.new("Illegal attribute '#{attribute}' specified") + end + response + else + raise Fog::Compute::AWS::NotFound.new("The network interface '#{network_interface_id}' does not exist") + end + end + end + end + end +end diff --git a/lib/fog/aws/requests/compute/describe_network_interfaces.rb b/lib/fog/aws/requests/compute/describe_network_interfaces.rb new file mode 100644 index 000000000..99dd1bbff --- /dev/null +++ b/lib/fog/aws/requests/compute/describe_network_interfaces.rb @@ -0,0 +1,85 @@ +module Fog + module Compute + class AWS + class Real + + require 'fog/aws/parsers/compute/describe_network_interfaces' + + # Describe all or specified network interfaces + # + # ==== Parameters + # * filters<~Hash> - List of filters to limit results with + # + # === Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * 'requestId'<~String> - Id of request + # * 'networkInterfaceSet'<~Array>: + # * 'networkInterfaceId'<~String> - The ID of the network interface + # * 'subnetId'<~String> - The ID of the subnet + # * 'vpcId'<~String> - The ID of the VPC + # * 'availabilityZone'<~String> - The availability zone + # * 'description'<~String> - The description + # * 'ownerId'<~String> - The ID of the person who created the interface + # * 'requesterId'<~String> - The ID ot teh entity requesting this interface + # * 'requesterManaged'<~String> - + # * 'status'<~String> - "available" or "in-use" + # * 'macAddress'<~String> - + # * 'privateIpAddress'<~String> - IP address of the interface within the subnet + # * 'privateDnsName'<~String> - The private DNS name + # * 'sourceDestCheck'<~Boolean> - Flag indicating whether traffic to or from the instance is validated + # * 'groupSet'<~Hash> - Associated security groups + # * 'key'<~String> - ID of associated group + # * 'value'<~String> - Name of associated group + # * 'attachment'<~Hash>: - Describes the way this nic is attached + # * 'attachmentID'<~String> + # * 'instanceID'<~String> + # * 'instanceOwnerId'<~String> + # * 'deviceIndex'<~Integer> + # * 'status'<~String> + # * 'attachTime'<~String> + # * 'deleteOnTermination'<~Boolean> + # * 'association'<~Hash>: - Describes an eventual instance association + # * 'attachmentID'<~String> - ID of the network interface attachment + # * 'instanceID'<~String> - ID of the instance attached to the network interface + # * 'publicIp'<~String> - Address of the Elastic IP address bound to the network interface + # * 'ipOwnerId'<~String> - ID of the Elastic IP address owner + # * 'tagSet'<~Array>: - Tags assigned to the resource. + # * 'key'<~String> - Tag's key + # * 'value'<~String> - Tag's value + # + # {Amazon API Reference}[http://docs.amazonwebservices.com/AWSEC2/2012-03-01/APIReference/index.html?ApiReference-query-DescribeNetworkInterfaces.html] + def describe_network_interfaces(filters = {}) + params = Fog::AWS.indexed_filters(filters) + request({ + 'Action' => 'DescribeNetworkInterfaces', + :idempotent => true, + :parser => Fog::Parsers::Compute::AWS::DescribeNetworkInterfaces.new + }.merge!(params)) + end + end + + + class Mock + + def describe_network_interfaces(filters = {}) + response = Excon::Response.new + + network_interface_info = self.data[:network_interfaces].values + + for filter_key, filter_value in filters + network_interface_info = network_interface_info.reject{|nic| ![*filter_value].include?(nic[filter_key])} + end + + response.status = 200 + response.body = { + 'requestId' => Fog::AWS::Mock.request_id, + 'networkInterfaceSet' => network_interface_info + } + response + end + + end + end + end +end diff --git a/lib/fog/aws/requests/compute/detach_network_interface.rb b/lib/fog/aws/requests/compute/detach_network_interface.rb new file mode 100644 index 000000000..6bfc24f52 --- /dev/null +++ b/lib/fog/aws/requests/compute/detach_network_interface.rb @@ -0,0 +1,49 @@ +module Fog + module Compute + class AWS + class Real + + require 'fog/aws/parsers/compute/detach_network_interface' + # Detaches a network interface. + # + # ==== Parameters + # * attachment_id<~String> - ID of the attachment to detach + # * force<~Boolean> - Set to true to force a detachment + # + # === Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * 'requestId'<~String> - Id of request + # * 'return'<~Boolean> - Returns true if the request succeeds. + # + # {Amazon API Reference}[http://docs.amazonwebservices.com/AWSEC2/2012-03-01/APIReference/ApiReference-query-DetachNetworkInterface.html] + def detach_network_interface(attachment_id, force = false) + request( + 'Action' => 'DetachNetworkInterface', + 'AttachmentId' => attachment_id, + 'Force' => force, + :parser => Fog::Parsers::Compute::AWS::DetachNetworkInterface.new + ) + end + end + + class Mock + def detach_network_interface(attachment_id) + response = Excon::Response.new + nic_id = self.data[:network_interfaces].select { |k,v| v['attachment']['attachmentId'] == attachment_id} .first.first + if nic_id + self.data[:network_interfaces][nic_id]["attachment"] = {} + response.status = 200 + response.body = { + 'requestId' => Fog::AWS::Mock.request_id, + 'return' => true + } + response + else + raise Fog::Compute::AWS::NotFound.new("The network interface '#{network_interface_id}' does not exist") + end + end + end + end + end +end diff --git a/lib/fog/aws/requests/compute/modify_network_interface_attribute.rb b/lib/fog/aws/requests/compute/modify_network_interface_attribute.rb new file mode 100644 index 000000000..2b2a294e2 --- /dev/null +++ b/lib/fog/aws/requests/compute/modify_network_interface_attribute.rb @@ -0,0 +1,90 @@ +module Fog + module Compute + class AWS + class Real + + require 'fog/aws/parsers/compute/basic' + + # Modifies a network interface attribute value + # + # ==== Parameters + # * network_interface_id<~String> - The ID of the network interface you want to describe an attribute of + # * attribute<~String> - The attribute to modify, must be one of 'description', 'groupSet', 'sourceDestCheck' or 'attachment' + # * value<~Object> - New value of attribute, the actual tyep depends on teh attribute: + # description - a string + # groupSet - a list of group id's + # sourceDestCheck - a boolean value + # attachment - a hash with: + # attachmentid - the attachment to change + # deleteOnTermination - a boolean + # + # {Amazon API Reference}[http://docs.amazonwebservices.com/AWSEC2/2012-03-01/APIReference/ApiReference-query-ModifyNetworkInterfaceAttribute.html] + def modify_network_interface_attribute(network_interface_id, attribute, value) + params = {} + case attribute + when 'description' + params['Description.Value'] = value + when 'groupSet' + params.merge!(Fog::AWS.indexed_param('SecurityGroupId.%d', value)) + when 'sourceDestCheck' + params['SourceDestCheck.Value'] = value + when 'attachment' + params['Attachment.AttachmentId'] = value['attachmentId'] + params['Attachment.DeleteOnTermination'] = value['deleteOnTermination'] + else + raise Fog::Compute::AWS::Error.new("Illegal attribute '#{attribute}' specified") + end + + request({ + 'Action' => 'ModifyNetworkInterfaceAttribute', + 'NetworkInterfaceId' => network_interface_id, + :parser => Fog::Parsers::Compute::AWS::Basic.new + }.merge!(params)) + end + end + + class Mock + def modify_network_interface_attribute(network_interface_id, attribute, value) + response = Excon::Response.new + if self.data[:network_interfaces][network_interface_id] + nic = self.data[:network_interfaces][network_interface_id] + + case attribute + when 'description' + nic['description'] = value.clone + when 'groupSet' + groups = {} + value.each do |group_id| + name = self.data[:security_groups].select { |k,v| v['groupId'] == group_id } .first.first + if name.nil? + raise Fog::Compute::AWS::Error.new("Unknown security group '#{group_id}' specified") + end + groups[group_id] = name + end + nic['groupSet'] = groups + when 'sourceDestCheck' + nic['sourceDestCheck'] = value + when 'attachment' + if nic['attachment'].nil? || value['attachmentId'] != nic['attachment']['attachmentId'] + raise Fog::Compute::AWS::Error.new("Illegal attachment '#{value['attachmentId']}' specified") + end + nic['attachment']['deleteOnTermination'] = value['deleteOnTermination'] + else + raise Fog::Compute::AWS::Error.new("Illegal attribute '#{attribute}' specified") + end + + response.status = 200 + response.body = { + 'requestId' => Fog::AWS::Mock.request_id, + 'return' => true + } + + response + else + raise Fog::Compute::AWS::NotFound.new("The network interface '#{network_interface_id}' does not exist") + end + end + end + end + end +end diff --git a/lib/fog/aws/requests/compute/reset_network_interface_attribute.rb b/lib/fog/aws/requests/compute/reset_network_interface_attribute.rb new file mode 100644 index 000000000..90c20e2a6 --- /dev/null +++ b/lib/fog/aws/requests/compute/reset_network_interface_attribute.rb @@ -0,0 +1,56 @@ +module Fog + module Compute + class AWS + class Real + + require 'fog/aws/parsers/compute/describe_network_interface_attribute' + # Resets a network interface attribute value + # + # ==== Parameters + # * network_interface_id<~String> - The ID of the network interface you want to describe an attribute of + # * attribute<~String> - The attribute to reset, only 'sourceDestCheck' is supported. + # + # === Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * 'requestId'<~String> - Id of request + # * 'return'<~Boolean> - success? + # + # {Amazon API Reference}[http://docs.amazonwebservices.com/AWSEC2/2012-03-01/APIReference/ApiReference-query-DescribeNetworkInterfaceAttribute.html] + def reset_network_interface_attribute(network_interface_id, attribute) + if attribute != 'sourceDestCheck' + raise Fog::Compute::AWS::Error.new("Illegal attribute '#{attribute}' specified") + end + request( + 'Action' => 'ResetNetworkInterfaceAttribute', + 'NetworkInterfaceId' => network_interface_id, + 'Attribute' => attribute, + :parser => Fog::Parsers::Compute::AWS::Basic.new + ) + end + end + + class Mock + def reset_network_interface_attribute(network_interface_id, attribute) + response = Excon::Response.new + if self.data[:network_interfaces][network_interface_id] + + response.status = 200 + response.body = { + 'requestId' => Fog::AWS::Mock.request_id, + 'return' => true + } + if attribute == 'sourceDestCheck' + self.data[:network_interfaces][network_interface_id]['sourceDestCheck'] = true + else + raise Fog::Compute::AWS::Error.new("Illegal attribute '#{attribute}' specified") + end + response + else + raise Fog::Compute::AWS::NotFound.new("The network interface '#{network_interface_id}' does not exist") + end + end + end + end + end +end diff --git a/tests/aws/models/compute/network_interfaces_test.rb b/tests/aws/models/compute/network_interfaces_test.rb new file mode 100644 index 000000000..3aef42190 --- /dev/null +++ b/tests/aws/models/compute/network_interfaces_test.rb @@ -0,0 +1,12 @@ +Shindo.tests("Fog::Compute[:aws] | network_interfaces", ['aws']) do + @vpc = Fog::Compute[:aws].vpcs.create('cidr_block' => '10.0.10.0/24') + @subnet = Fog::Compute[:aws].subnets.create('vpc_id' => @vpc.id, 'cidr_block' => '10.0.10.16/28') + @subnet_id = @subnet.subnet_id + + collection_tests(Fog::Compute[:aws].network_interfaces, + {:description => 'nic_desc', :name => 'nic_name', :subnet_id => @subnet_id}, + true) + + @subnet.destroy + @vpc.destroy +end diff --git a/tests/aws/requests/compute/network_interface_tests.rb b/tests/aws/requests/compute/network_interface_tests.rb new file mode 100644 index 000000000..dc620c87a --- /dev/null +++ b/tests/aws/requests/compute/network_interface_tests.rb @@ -0,0 +1,171 @@ +Shindo.tests('Fog::Compute[:aws] | network interface requests', ['aws']) do + + @network_interface_format = { + 'networkInterfaceId' => String, + 'subnetId' => String, + 'vpcId' => String, + 'availabilityZone' => String, + 'description' => Fog::Nullable::String, + 'ownerId' => String, + 'requesterId' => Fog::Nullable::String, + 'requesterManaged' => String, + 'status' => String, + 'macAddress' => String, + 'privateIpAddress' => String, + 'privateDnsName' => Fog::Nullable::String, + 'sourceDestCheck' => Fog::Boolean, + 'groupSet' => Fog::Nullable::Hash, + 'attachment' => Hash, + 'association' => Hash, + 'tagSet' => Hash + } + + @network_interface_create_format = { + 'networkInterface' => @network_interface_format, + 'requestId' => String + } + + @network_interfaces_format = { + 'requestId' => String, + 'networkInterfaceSet' => [ @network_interface_format ] + } + + @attach_network_interface_format = { + 'requestId' => String, + 'attachmentId' => String + } + + tests('success') do + # Create environment + @vpc = Fog::Compute[:aws].vpcs.create('cidr_block' => '10.0.10.0/24') + @subnet = Fog::Compute[:aws].subnets.create('vpc_id' => @vpc.id, 'cidr_block' => '10.0.10.16/28') + @security_group = Fog::Compute[:aws].security_groups.create('name' => 'sg_name', 'description' => 'sg_desc', 'vpc_id' => @vpc.id) + + + @subnet_id = @subnet.subnet_id + @security_group_id = @security_group.group_id + + DESCRIPTION = "Small and green" + tests("#create_network_interface(#{@subnet_id})").formats(@network_interface_create_format) do + data = Fog::Compute[:aws].create_network_interface(@subnet_id, {"PrivateIpAddress" => "10.0.10.23"}).body + @nic_id = data['networkInterface']['networkInterfaceId'] + data + end + + # Describe network interfaces + tests('#describe_network_interfaces').formats(@network_interfaces_format) do + Fog::Compute[:aws].describe_network_interfaces.body + end + + # Describe network interface attribute + tests("#describe_network_interface_attribute(#{@nic_id}, 'description')").returns(nil) do + Fog::Compute[:aws].describe_network_interface_attribute(@nic_id, 'description').body['description'] + end + + # test describe of all supported attributes + [ 'description', 'groupSet', 'sourceDestCheck', 'attachment'].each do |attrib| + tests("#describe_network_interface_attribute(#{@nic_id}, #{attrib})").returns(@nic_id) do + Fog::Compute[:aws].describe_network_interface_attribute(@nic_id, attrib).body['networkInterfaceId'] + end + end + + # Modify network interface description attribute + tests("#modify_network_interface_attribute(#{@nic_id}, 'description', '#{DESCRIPTION}')").returns(true) do + Fog::Compute[:aws].modify_network_interface_attribute(@nic_id, 'description', DESCRIPTION).body["return"] + end + + # Describe network interface attribute again + tests("#describe_network_interface_attribute(#{@nic_id}, 'description')").returns(DESCRIPTION) do + Fog::Compute[:aws].describe_network_interface_attribute(@nic_id, 'description').body["description"] + end + + # Restore network interface description attribute + tests("#modify_network_interface_attribute(#{@nic_id}, 'description', '')").returns(true) do + Fog::Compute[:aws].modify_network_interface_attribute(@nic_id, 'description', '').body["return"] + end + + # Check modifying the group set + tests("#modify_network_interface_attribute(#{@nic_id}, 'groupSet', [#{@security_group_id}])").returns(true) do + Fog::Compute[:aws].modify_network_interface_attribute(@nic_id, 'groupSet', [@security_group_id]).body["return"] + end + tests("#describe_network_interface_attribute(#{@nic_id}, 'groupSet')").returns({ @security_group_id => "sg_name" }) do + Fog::Compute[:aws].describe_network_interface_attribute(@nic_id, 'groupSet').body["groupSet"] + end + + # Check modifying the source dest check (and reset) + tests("#modify_network_interface_attribute(#{@nic_id}, 'sourceDestCheck', false)").returns(true) do + Fog::Compute[:aws].modify_network_interface_attribute(@nic_id, 'sourceDestCheck', false).body["return"] + end + tests("#describe_network_interface_attribute(#{@nic_id}, 'sourceDestCheck')").returns(false) do + Fog::Compute[:aws].describe_network_interface_attribute(@nic_id, 'sourceDestCheck').body["sourceDestCheck"] + end + tests("#reset_network_interface_attribute(#{@nic_id}, 'sourceDestCheck')").returns(true) do + Fog::Compute[:aws].reset_network_interface_attribute(@nic_id, 'sourceDestCheck').body["return"] + end + tests("#describe_network_interface_attribute(#{@nic_id}, 'sourceDestCheck')").returns(true) do + Fog::Compute[:aws].describe_network_interface_attribute(@nic_id, 'sourceDestCheck').body["sourceDestCheck"] + end + + @server = Fog::Compute[:aws].servers.create({:flavor_id => 'm1.small', :subnet_id => @subnet_id }) + @server.wait_for { ready? } + @instance_id=@server.id + + # attach + tests('#attach_network_interface').formats(@attach_network_interface_format) do + data = Fog::Compute[:aws].attach_network_interface(@nic_id, @instance_id, 1).body + @attachment_id = data['attachmentId'] + data + end + + # Check modifying the attachment + attach_attr = { + 'attachmentId' => @attachment_id, + 'deleteOnTermination' => true + } + tests("#modify_network_interface_attribute(#{@nic_id}, 'attachment', #{attach_attr.inspect})").returns(true) do + Fog::Compute[:aws].modify_network_interface_attribute(@nic_id, 'attachment', attach_attr).body["return"] + end + + # detach + tests('#detach_network_interface').returns(true) do + Fog::Compute[:aws].detach_network_interface(@attachment_id,true).body["return"] + end + Fog::Compute[:aws].network_interfaces.get(@nic_id).wait_for { status == 'available'} + # Create network interface with arguments + options = { + "PrivateIpAddress" => "10.0.10.24", + "Description" => DESCRIPTION, + "GroupSet" => [@security_group_id] + } + tests("#create_network_interface(#{@subnet_id}), #{options.inspect}").returns("10.0.10.24") do + data = Fog::Compute[:aws].create_network_interface(@subnet_id, options).body + @nic2_id = data['networkInterface']['networkInterfaceId'] + data['networkInterface']['privateIpAddress'] + end + + # Check assigned values + tests("#describe_network_interface_attribute(#{@nic2_id}, 'description')").returns(DESCRIPTION) do + Fog::Compute[:aws].describe_network_interface_attribute(@nic2_id, 'description').body["description"] + end + tests("#describe_network_interface_attribute(#{@nic2_id}, 'groupSet'')").returns({ @security_group_id => "sg_name"}) do + Fog::Compute[:aws].describe_network_interface_attribute(@nic2_id, 'groupSet').body["groupSet"] + end + + # Delete network interfaces + tests("#delete_network_interface('#{@nic2_id}')").formats(AWS::Compute::Formats::BASIC) do + Fog::Compute[:aws].delete_network_interface(@nic2_id).body + end + tests("#delete_network_interface('#{@nic_id}')").formats(AWS::Compute::Formats::BASIC) do + Fog::Compute[:aws].delete_network_interface(@nic_id).body + end + + # Clean up resources + @server.destroy + @server.wait_for { state == 'terminated' } + # despite the fact that the state goes to 'terminated' we need a little delay for aws to do its thing + sleep 5 + @security_group.destroy + @subnet.destroy + @vpc.destroy + end +end