diff --git a/features/examples/topology.change-history.feature b/features/examples/topology.change-history.feature deleted file mode 100644 index 9f1f33225..000000000 --- a/features/examples/topology.change-history.feature +++ /dev/null @@ -1,38 +0,0 @@ -Feature: Topology Ruby API example - - The change-history.rb example ([trema]/src/examples/topology/change-history.rb) - is a sample controller which uses Topology Ruby API. - - @slow_process - Scenario: Run the Ruby example - Given a file named "change-history.conf" with: - """ - 1.upto( 3 ).each do | sw | - vswitch { dpid sw } - 1.upto( sw - 1 ).each do | peer | - link "%#x" % sw, "%#x" % peer - end - end - - run { - path "../../objects/topology/topology" - } - - run { - path "../../objects/examples/dumper/dumper" - } - - event :port_status => "topology", :packet_in => "filter", :state_notify => "topology" - filter :lldp => "topology", :packet_in => "dumper" - """ - When I run `../../trema run ../../src/examples/topology/change-history.rb -c change-history.conf -d` - Then wait until "topology" is up - Then *** sleep 32 *** - - Then the file "change-history.dot" should match /.*graph \[label="Gen \d+\\n\(0x3 -> 0x1\) up"\];.*/ - Then the file "change-history.dot" should match /.*graph \[label="Gen \d+\\n\(0x3 -> 0x2\) up"\];.*/ - Then the file "change-history.dot" should match /.*graph \[label="Gen \d+\\n\(0x2 -> 0x1\) up"\];.*/ - Then the file "change-history.dot" should match /.*graph \[label="Gen \d+\\n\(0x2 -> 0x3\) up"\];.*/ - Then the file "change-history.dot" should match /.*graph \[label="Gen \d+\\n\(0x1 -> 0x2\) up"\];.*/ - Then the file "change-history.dot" should match /.*graph \[label="Gen \d+\\n\(0x1 -> 0x3\) up"\];.*/ - \ No newline at end of file diff --git a/features/topology/topology.map.feature b/features/topology/topology.map.feature deleted file mode 100644 index 6110a1b81..000000000 --- a/features/topology/topology.map.feature +++ /dev/null @@ -1,167 +0,0 @@ -Feature: topology map Ruby API - - As a developer using Trema - I want to develop topology aware application. - - Scenario: map_ready handler - Given a file named "topology.conf" with: - """ - vswitch("topology1") { datapath_id "0x1" } - - run { - path "../../objects/topology/topology" - options "--always_run_discovery" - } - - event :port_status => "topology", :packet_in => "filter", :state_notify => "topology" - filter :lldp => "topology", :packet_in => "MapReadyTestTopology" - """ - And a file named "MapReadyTestTopology.rb" with: - """ - require "trema/topology/map_api" - - class MapReadyTestTopology < Controller - include TopologyMap - - oneshot_timer_event :test_start, 0 - - def test_start - send_rebuild_map_request - end - - def map_ready c - info "map_ready" - end - end - """ - When I run `trema run MapReadyTestTopology.rb -c topology.conf -d` - And wait until "topology" is up - And *** sleep 1 *** - Then the file "../../tmp/log/MapReadyTestTopology.log" should contain "map_ready" - - Scenario: map_ready handler (block) - Given a file named "topology.conf" with: - """ - vswitch("topology1") { datapath_id "0x1" } - - run { - path "../../objects/topology/topology" - options "--always_run_discovery" - } - - event :port_status => "topology", :packet_in => "filter", :state_notify => "topology" - filter :lldp => "topology", :packet_in => "MapReadyTestTopologyB" - """ - And a file named "MapReadyTestTopologyB.rb" with: - """ - require "trema/topology/map_api" - - class MapReadyTestTopologyB < Controller - include TopologyMap - - oneshot_timer_event :test_start, 0 - - def test_start - send_rebuild_map_request { |map| - info "map_ready_block" - } - end - - def map_ready c - info "map_ready_should_not_be_called" - end - end - """ - When I run `trema run MapReadyTestTopologyB.rb -c topology.conf -d` - And wait until "topology" is up - And *** sleep 1 *** - Then the file "../../tmp/log/MapReadyTestTopologyB.log" should contain "map_ready_block" - And the file "../../tmp/log/MapReadyTestTopologyB.log" should not contain "map_ready_should_not_be_called" - - Scenario: get map without rebuilding - Given a file named "topology.conf" with: - """ - vswitch("topology1") { datapath_id "0x1" } - vswitch("topology2") { datapath_id "0x2" } - - link "topology1", "topology2" - - run { - path "../../objects/topology/topology" - options "--always_run_discovery" - } - - event :port_status => "topology", :packet_in => "filter", :state_notify => "topology" - filter :lldp => "topology", :packet_in => "RebuildTestController" - """ - And a file named "RebuildTestController.rb" with: - """ - require "trema/topology/map_api" - - class RebuildTestController < Controller - include TopologyMap - - oneshot_timer_event :delayed_event, 4 - - def delayed_event - send_rebuild_map_request false - end - - def map_ready c - info c.to_s - end - end - """ - When I run `trema run RebuildTestController.rb -c topology.conf -d` - And wait until "topology" is up - And *** sleep 4 *** - Then the file "../../tmp/log/RebuildTestController.log" should contain: - """ - Map: - """ - And the file "../../tmp/log/RebuildTestController.log" should contain: - """ - Switch: 0x1 - {up:true} - """ - And the file "../../tmp/log/RebuildTestController.log" should match / Port: 0x1:1 - \{external:false, mac:"[0-9a-f:]+", name:".*", up:true\}/ - And the file "../../tmp/log/RebuildTestController.log" should contain: - """ - Links_in - <= 0x2:1 - """ - And the file "../../tmp/log/RebuildTestController.log" should contain: - """ - Links_out - => 0x2:1 - """ - And the file "../../tmp/log/RebuildTestController.log" should contain: - """ - Switch: 0x2 - {up:true} - """ - And the file "../../tmp/log/RebuildTestController.log" should match / Port: 0x2:1 - \{external:false, mac:"[0-9a-f:]+", name:".*", up:true\}/ - And the file "../../tmp/log/RebuildTestController.log" should contain: - """ - Links_in - <= 0x1:1 - """ - And the file "../../tmp/log/RebuildTestController.log" should contain: - """ - Links_out - => 0x1:1 - """ - And the file "../../tmp/log/RebuildTestController.log" should contain: - """ - Port: 0x1:1 - { - """ - And the file "../../tmp/log/RebuildTestController.log" should contain: - """ - Port: 0x2:1 - { - """ - And the file "../../tmp/log/RebuildTestController.log" should contain: - """ - Link: (0x1:1)->(0x2:1) - {unstable:false, up:true} - """ - And the file "../../tmp/log/RebuildTestController.log" should contain: - """ - Link: (0x2:1)->(0x1:1) - {unstable:false, up:true} - """ diff --git a/ruby/trema/topology.c b/ruby/trema/topology.c index 5d10b35c7..8f195e920 100644 --- a/ruby/trema/topology.c +++ b/ruby/trema/topology.c @@ -91,7 +91,6 @@ static VALUE switch_status_to_hash( const topology_switch_status* sw_status ) { VALUE sw = rb_hash_new(); rb_hash_aset( sw, ID2SYM( rb_intern( "dpid" ) ), ULL2NUM( sw_status->dpid ) ); -// rb_hash_aset( sw, ID2SYM( rb_intern( "status" ) ), INT2FIX( (int)sw_status->status ) ); if ( sw_status->status == TD_SWITCH_UP ) { rb_hash_aset( sw, ID2SYM( rb_intern( "up" ) ), Qtrue ); @@ -124,8 +123,6 @@ port_status_to_hash( const topology_port_status* port_status ) { const uint8_t* mac = port_status->mac; snprintf( macaddr, sizeof(macaddr), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] ); rb_hash_aset( port, ID2SYM( rb_intern( "mac" ) ), rb_str_new2( macaddr ) ); -// rb_hash_aset( port, ID2SYM( rb_intern( "external" ) ), INT2FIX( (int)port_status->external ) ); -// rb_hash_aset( port, ID2SYM( rb_intern( "status" ) ), INT2FIX( (int)port_status->status ) ); if ( port_status->external == TD_PORT_EXTERNAL ) { rb_hash_aset( port, ID2SYM( rb_intern( "external" ) ), Qtrue ); @@ -160,7 +157,6 @@ link_status_to_hash( const topology_link_status* link_status ) { rb_hash_aset( link, ID2SYM( rb_intern( "from_portno" ) ), INT2FIX( (int)link_status->from_portno ) ); rb_hash_aset( link, ID2SYM( rb_intern( "to_dpid" ) ), ULL2NUM( link_status->to_dpid ) ); rb_hash_aset( link, ID2SYM( rb_intern( "to_portno" ) ), INT2FIX( (int)link_status->to_portno ) ); -// rb_hash_aset( link, ID2SYM( rb_intern( "status" ) ), INT2FIX( (int)link_status->status ) ); if ( link_status->status != TD_LINK_DOWN ) { rb_hash_aset( link, ID2SYM( rb_intern( "up" ) ), Qtrue ); @@ -504,33 +500,6 @@ topology_send_all_switch_status( VALUE self ) { void Init_topology( void ) { mTopology = rb_define_module_under( mTrema, "Topology" ); - /* @!group enum topology_switch_status_type */ - /* Same as C API enum topology_switch_status_type */ - rb_define_const( mTopology, "TD_SWITCH_DOWN", INT2NUM( TD_SWITCH_DOWN ) ); - /* Same as C API enum topology_switch_status_type */ - rb_define_const( mTopology, "TD_SWITCH_UP", INT2NUM( TD_SWITCH_UP ) ); - - /* @!group enum topology_port_status_type */ - /* Same as C API enum topology_port_status_type */ - rb_define_const( mTopology, "TD_PORT_DOWN", INT2NUM( TD_PORT_DOWN ) ); - /* Same as C API enum topology_port_status_type */ - rb_define_const( mTopology, "TD_PORT_UP", INT2NUM( TD_PORT_UP ) ); - - /* @!group enum topology_port_external_type */ - /* Same as C API enum topology_port_external_type */ - rb_define_const( mTopology, "TD_PORT_INACTIVE", INT2NUM( TD_PORT_INACTIVE ) ); - /* Same as C API enum topology_port_external_type */ - rb_define_const( mTopology, "TD_PORT_EXTERNAL", INT2NUM( TD_PORT_EXTERNAL ) ); - - /* @!group enum topology_link_status_type */ - /* Same as C API enum topology_link_status_type */ - rb_define_const( mTopology, "TD_LINK_DOWN", INT2NUM( TD_LINK_DOWN ) ); - /* Same as C API enum topology_link_status_type */ - rb_define_const( mTopology, "TD_LINK_UP", INT2NUM( TD_LINK_UP ) ); - /* Same as C API enum topology_link_status_type */ - rb_define_const( mTopology, "TD_LINK_UNSTABLE", INT2NUM( TD_LINK_UNSTABLE ) ); - /* @!endgroup */ - rb_define_protected_method( mTopology, "init_libtopology", topology_init_libtopology, 1 ); rb_define_protected_method( mTopology, "finalize_libtopology", topology_finalize_libtopology, 0 ); diff --git a/ruby/trema/topology.rb b/ruby/trema/topology.rb index 7304ac34b..9edfc59a3 100644 --- a/ruby/trema/topology.rb +++ b/ruby/trema/topology.rb @@ -220,7 +220,7 @@ def subscribed? # def start # init_libtopology "topology" # send_subscribe_topology if topology_handler_implemented? - # + # send_enable_topology_discovery if respond_to?( :link_status_updated ) # # your application's pre-start_trema() call initialization here. # end # end diff --git a/ruby/trema/topology/guarded-property-accessor.rb b/ruby/trema/topology/guarded-property-accessor.rb deleted file mode 100644 index 68229bcb2..000000000 --- a/ruby/trema/topology/guarded-property-accessor.rb +++ /dev/null @@ -1,61 +0,0 @@ -# -# Copyright (C) 2008-2013 NEC Corporation -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License, version 2, as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - - -module Trema - module Topology - module GuardedPropertyAccesor - - # Accessor for the property value of this node - def []( key ) - return @property[key] - end - - - # Overwrite a property value of this node - # @raise [ArgumentError] if key was a mandatory key of a node - def []=( key, value ) - raise ArgumentError, "Overwriting #{key} is not allowed" if self.class.is_mandatory_key?(key) - return @property[key] = value - end - - - # Delete a property of this node. - # mandatory key will be treated as if it didn't exist. - def delete( key ) - if block_given? then - yield key if not @property.has_key?(key) or self.class.is_mandatory_key?(key) - end - return nil if self.class.is_mandatory_key?(key) - return deleted = @property.delete(key) - end - - - # Update properties of this node using specified Hash or node - # mandatory keys will be ignored - def update( other ) - other = other.properties if other.is_a?(self.class) - other = other.to_hash if not other.is_a?(Hash) - - other.each_pair do |key,value| - @property[key] = value if not self.class.is_mandatory_key?(key) - end - end - alias :merge! :update - end - end -end \ No newline at end of file diff --git a/ruby/trema/topology/link.rb b/ruby/trema/topology/link.rb index d228e3dd5..b3e11b6ab 100644 --- a/ruby/trema/topology/link.rb +++ b/ruby/trema/topology/link.rb @@ -70,7 +70,7 @@ def unstable? end - # @return [Array(Integer,Integer,Integer,Integer)] Link key 4-tuple for this Link instance + # @return [[Integer,Integer,Integer,Integer]] Link key 4-tuple for this Link instance def key return [ from_dpid, from_portno, to_dpid, to_portno ] end @@ -82,7 +82,6 @@ def key # @option link [Integer] :from_portno port number of switch which this link departs from # @option link [Integer] :to_dpid Switch dpid which this link peer to # @option link [Integer] :to_portno port number of switch which this link peer to - # @return [Link] # @example # link = Link.new( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72 } ) def initialize( link ) @@ -125,10 +124,11 @@ def to_s def property_to_s - kvp_ary = @property.select { |key,_| not Link.is_mandatory_key?( key ) } - kvp_ary = kvp_ary.sort_by { |key,_| key.to_s } - s = kvp_ary.map { |key, val| "#{key.to_s}:#{val.inspect}" }.join(", ") - return s + kvp_ary = @property.select { |key,_| not Link.is_mandatory_key?( key ) }. + map { |key, val| [key.to_s, val] }. + sort { |lhs,rhs| lhs.first <=> rhs.first }. + map { |key, val| "#{key}:#{val.inspect}"} + return kvp_ary.join(", ") end end end diff --git a/ruby/trema/topology/map.rb b/ruby/trema/topology/map.rb deleted file mode 100644 index ea731d659..000000000 --- a/ruby/trema/topology/map.rb +++ /dev/null @@ -1,219 +0,0 @@ -# -# Copyright (C) 2008-2013 NEC Corporation -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License, version 2, as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - - -module Trema - module Topology - # Topology Map structure - class Map - # @return [{Integer=>Switch}] Hash of Switches: dpid => Switch - # @note Do not directly add/remove elements in this Hash. - attr_reader :switches - # @return [{[Integer,Integer,Integer,Integer]=>Link}] Hash of Links: [from.dpid, from.port_no, to.dpid, to.port_no] => Links - # @note Do not directly add/remove elements in this Hash. - attr_reader :links - # Create empty Map - def initialize - @switches = Hash.new - @links = Hash.new - end - - # @!group Switch manipulation methods - - - # Add a switch to topology map. - # @return [Switch] Switch instance added. - def add_switch sw - raise TypeError, "Trema::Topology::Switch expected" if not sw.is_a?(Switch) - @switches[ sw.dpid ] = sw - end - - - # Delete a switch from Topology map. - # @note Links from/to the switch will also be removed - # @param [Switch,Hash] sw Switch instance or a Hash with Switch info - def del_switch sw - del_switch_by_dpid sw[:dpid] - end - - - # Delete a switch from Topology map using dpid - # @see #del_switch - def del_switch_by_dpid dpid - remove_links = @links.select { |key,_| (key[FROM_DPID] == dpid || key[TO_DPID] == dpid) } - remove_links.each { |kv_pair| self.del_link( kv_pair.first ) } - @switches.delete dpid - end - - - # Get a switch from Topology map using dpid. - # Switch instance will be created if not found. - # @param [Integer] dpid dpid of the switch to look for. - # @return [Switch] Switch instance for specified dpid - def get_switch_for_dpid dpid - sw = lookup_switch_by_dpid dpid - sw ||= add_switch Switch.new( { :dpid => dpid } ) - return sw - end - - # @!group Link manipulation methods - - - # Add a link to Topology map. - # @note Corresponding Switch object's links_out, links_in will also be updated. - def add_link link - raise TypeError, "Trema::Topology::Link expected" if not link.is_a?(Link) - - key = link.key - key.each { |each| each.freeze } - key.freeze - - sw_from = get_switch_for_dpid link.from_dpid - sw_to = get_switch_for_dpid link.to_dpid - - sw_from.add_link link - sw_to.add_link link - @links[ key ] = link - end - - - # Delete a link from Topology map. - # @note Corresponding Switch object's links_out, links_in will also be updated. - # @param [Link,Hash] link Link instance or a Hash with link info. - def del_link link - del_link_by_key [ link[:from_dpid], link[:from_portno], link[:to_dpid], link[:to_portno] ] - end - - - # Delete a link from Topology map. - # @param [Array(Integer,Integer,Integer,Integer)] key - # 4 element array. [from.dpid, from.port_no, to.dpid, to.port_no] - def del_link_by_key key - sw_from = @switches[ key[FROM_DPID] ]; - sw_to = @switches[ key[TO_DPID] ]; - - sw_from.delete_link( key ) if sw_from - sw_to.delete_link( key ) if sw_to - @links.delete( key ) - end - - # - # @!group Lookup map contents - # - - # Lookup a switch from Topology map using dpid - # @param [Integer] dpid dpid of the switch to look for. - # @return [Switch] Switch instance found, or nil if not found. - def lookup_switch_by_dpid dpid - @switches[dpid] - end - - - # Lookup a link from Topology map. - # @param [Hash] link look up a link instance using key elements listed in Options - # @option (see Link#initialize) - # @return [Link] Link instance found, or nil if not found. - def lookup_link_by_hash link - key = [ link[:from_dpid], link[:from_portno], link[:to_dpid], link[:to_portno] ]; - return @links[ key ] - end - - - # - # @!group Update by Hash methods - # - - # Update Switch instance. Switch instance will be created if it does not exist. - # Switch instance will be removed if the state is not up - # @param [Switch,Hash] sw Switch instance or a Hash with switch info - # @option (see Switch#initialize) - def update_switch sw - sw = sw.property if sw.is_a?(Switch) - raise ArgumentError, "Mandatory key element for Switch missing in Hash" if not Switch.has_mandatory_keys?( sw ) - - dpid = sw[:dpid] - if sw[:up] then - s = lookup_switch_by_dpid( dpid ) - if s != nil then - s.update( sw ) - else - add_switch Switch.new( sw ) - end - else - del_switch_by_dpid dpid - end - end - - - # Update Link instance. Link instance will be created if it does not exist. - # Link instance will be removed if the state is not up - # @param [Link,Hash] link Link instance or a Hash with link info - # @option (see Link#initialize) - def update_link link - link = link.property if link.is_a?(Link) - raise ArgumentError, "Mandatory key element for Link missing in Hash" if not Link.has_mandatory_keys?( link ) - - if link[:up] then - l = lookup_link_by_hash( link ) - if l != nil then - l.update( link ) - else - add_link Link.new( link ) - end - else - del_link link - end - end - - - # Update Port instance. Port instance will be created if it does not exist. - # Port instance will be removed if the state is not up - # @param [Port,Hash] port Port instance or a Hash with port info - # @option (see Port#initialize) - def update_port port - port = port.property if port.is_a?(Port) - raise ArgumentError, "Mandatory key element for Port missing in Hash" if not Port.has_mandatory_keys?( port ) - - dpid = port[:dpid] - s = lookup_switch_by_dpid( dpid ) - if port[:up] then - s ||= add_switch Switch.new( { :dpid => dpid } ) - end - s.update_port( port ) if s != nil - end - - # - # @!endgroup - # - - # Dump map info as a String - # @return [String] content of this map. - def to_s - s = "Map:\n" - s << "(Empty)\n" if @switches.empty? and @links.empty? - @switches.each_pair do |_, sw| - s << sw.to_s - end - @links.each_pair do |_, link| - s << "#{ link.to_s }\n" - end - return s - end - - end - end -end diff --git a/ruby/trema/topology/map_api.rb b/ruby/trema/topology/map_api.rb deleted file mode 100644 index 66f7efae9..000000000 --- a/ruby/trema/topology/map_api.rb +++ /dev/null @@ -1,339 +0,0 @@ -# -# Copyright (C) 2008-2013 NEC Corporation -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License, version 2, as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -require 'trema/topology' -require 'trema/topology/map' -require 'trema/topology/guarded-property-accessor' - - -module Trema - - module Topology - class Switch - include Topology::GuardedPropertyAccesor - - # @!group Additional methods for Map maintenance - - # @return [Hash] Raw access to properties of a Switch. User must not modify mandatory key elements in Hash. - attr_reader :property - - - # @return [String] Switch key as a String - def key_str - return "S#{ dpid.to_s(16) }" - end - - - # @!attribute [w] up - # @param [Boolean] new_state_up - # @return [Boolean] returns new state - def up= new_state_up - if new_state_up then - @property[:up] = true - else - @property[:up] = false - end - end - - - # Update port on this switch - # @overload update_port port - # @param [Port] port Port instance to update - # @overload update_port port_hash - # @param [Hash] port_hash a Hash with port info about instance to update - # @option (see Port#initialize) - def update_port port - port = port.property if port.is_a?(Port) - raise ArgumentError, "Mandatory key element for Port missing in Hash" if not Port.has_mandatory_keys?( port ) - - portno = port[:portno] - if port[:up] then - if @ports.include?( portno ) then - @ports[ portno ].update port - else - add_port Port.new( port ) - end - else - @ports.delete( portno ) - end - end - end - - class Port - include Topology::GuardedPropertyAccesor - - # @!group Additional methods for Map maintenance - - # @return [Hash] Raw access to properties of a Port. User must not modify mandatory key elements in Hash. - attr_reader :property - - - # @return [String] Port key as a String - def key_str - return "P#{ dpid.to_s(16) }-#{ portno.to_s }" - end - end - - class Link - include Topology::GuardedPropertyAccesor - - # @!group Additional methods for Map maintenance - - # @return [Hash] Raw access to properties of a Link. User must not modify mandatory key elements in Hash. - attr_reader :property - - - # @return [String] Link key as a String - def key_str - return "L#{ from_dpid.to_s(16) }-#{ from_portno.to_s }-#{ to_dpid.to_s(16) }-#{ to_portno.to_s }" - end - end - end - - # Module to add Topology Map feature - module TopologyMap - include Topology - - # - # @private Just a placeholder for YARD. - # - def self.handler name - # Do nothing. - end - - # @!group Basic Map events and methods - # - # @example - # class TestController < Controller - # include Topology - # - # oneshot_timer_event :on_start, 0 - # def on_start - # send_enable_topology_discovery - # send_rebuild_map_request - # end - # - # def map_ready map - # info "Topology Map ready!" - # p map - # - # # You can do whatever with map after this point. - # # Topology::Map instance can also be obtained later using #get_map method. - # - # # example: - # # build spanning trees for each vertices - # end - # - # def link_status_updated link - # # Directly read Hash containing link info. - # p link[:from_dpid] - # # Or, create Topology::Link instance. - # linkObj = Link.new( links ) - # p linkObj.from_dpid - # - # # Do what ever before Topology Map update - # # Note: Link instance will be removed after map update, - # # if the notified link state was not up. - # - # # example: - # if not linkObj.up? then - # # Link down event: - # # 1. drop flow path, which contains this link. - # # 2. resolve alternate path with updated topology. - # # 3. set newly calculated flow path. - # else - # # Link up event: - # # 1. PathResolver rebuild spanning trees. - # # 2. replace existing path by newly calculated path using using FlowManager::libPath? - # end - # - # # (Optional) Manually update Map - # # Note: Map will be automatically updated after exit from this handler - # # even if this manual update - # update_map_by_link_hash link - # - # if map_ready? - # # Check to see if we're at a point after map_ready event. - # - # # Do whatever with updated map. - # puts get_map - # end - # end - # end - # - # Start rebuilding topology map. - # `map_ready` or specified block will be called, when map rebuild is complete. - # @param [Boolean] clear_map Clear map before update. - # @yieldparam map [Map] Current map - # @return [Boolean] true if request sent successfully - def send_rebuild_map_request clear_map=true, &block - @need_map_ready_notify = true - @map_up_to_date = false - @map = Map.new if clear_map - @all_link_received = false - @all_switch_received = false - @all_port_received = false - - succ = true - succ &= get_all_switch_status do |switches| - switches.each { |sw| update_map_by_switch_hash(sw) } - @all_switch_received = true - notify_map_ready( &block ) if @need_map_ready_notify and map_ready? - end - - succ &= get_all_port_status do |ports| - ports.each { |port| update_map_by_port_hash(port) } - @all_port_received = true - notify_map_ready( &block ) if @need_map_ready_notify and map_ready? - end - - succ &= get_all_link_status do |links| - links.each { |link| update_map_by_link_hash(link) } - @all_link_received = true - notify_map_ready( &block ) if @need_map_ready_notify and map_ready? - end - return succ - end - - - # - # @!method map_ready( map ) - # Event handler for map_ready event. - # @abstract map_ready event handler. Override this to implement a custom handler. - # @param [Map] map - # @return [void] - # Reference to current topology map. - # @see #send_rebuild_map_request See #send_rebuild_map_request for usage example. - handler :map_ready - - - # - # Returns a reference to latest map available. - # @note Returned instance may be outdated, when invoked inside Topology update event handlers - # before manually update of the map. - # @return [Map] Returns a reference to available map. - # - # @see #get_map - # - def get_last_map - return @map - end - - # - # Returns a reference to current map when available. - # @note This method will return nil when a updated map is not ready. - # @return [Map] Returns a reference to latest map or nil, when current map is not available. - # - # @example - # def link_status_updated lnk - # get_last_map # returns a outdated network map. - # get_map # returns nil - # update_map_by_link_hash lnk - # get_last_map # returns the current map - # get_map # returns the current map - # end - # - def get_map - return @map if map_up_to_date? - return nil - end - - - # Check if map is ready to use. - def map_ready? - @all_link_received and @all_switch_received and @all_port_received - end - - - # Check if current map is in latest state. - def map_up_to_date? - map_ready? and @map_up_to_date - end - - - # Call inside switch_status_updated handler to manually update map to latest state - # @note Map will be automatically updated even if this method call was omitted. - # @param [Hash] sw Specify switch hash given to switch_status_updated. Additional properties may be added to reflect internal map. - # @return [void] - # @see #send_rebuild_map_request See #send_rebuild_map_request for usage example. - def update_map_by_switch_hash sw - @map ||= Topology::Map.new - @map.update_switch( sw ) - @map_up_to_date = true - end - - - # Call inside link_status_updated handler to manually update map to latest state - # @note Map will be automatically updated even if this method call was omitted. - # @param [Hash] link Specify link hash given to link_status_updated. Additional properties may be added to reflect internal map. - # @return [void] - # @see #send_rebuild_map_request See #send_rebuild_map_request for usage example. - def update_map_by_link_hash link - @map ||= Topology::Map.new - @map.update_link( link ) - @map_up_to_date = true - end - - - # Call inside port_status_updated handler to manually update map to latest state - # @note Map will be automatically updated even if this method call was omitted. - # @param [Hash] port Specify link hash given to port_status_updated. Additional properties may be added to reflect internal map. - # @return [void] - # @see #send_rebuild_map_request See #send_rebuild_map_request for usage example. - def update_map_by_port_hash port - @map ||= Topology::Map.new - @map.update_port( port ) - @map_up_to_date = true - end - - ###################### - protected - ###################### - - - alias_method :super_switch_status_updated, :_switch_status_updated - def _switch_status_updated sw - super_switch_status_updated( sw ) - update_map_by_switch_hash( sw ) - end - - - alias_method :super_port_status_updated, :_port_status_updated - def _port_status_updated port - super_port_status_updated( port ) - update_map_by_port_hash( port ) - end - - - alias_method :super_link_status_updated, :_link_status_updated - def _link_status_updated link - super_link_status_updated( link ) - update_map_by_link_hash( link ) - end - - - def notify_map_ready &block - if block then - block.call( @map ) - elsif self.respond_to? :map_ready then - map_ready @map - end - @need_map_ready_notify = false - end - end -end - diff --git a/ruby/trema/topology/port.rb b/ruby/trema/topology/port.rb index 997365907..77b6022f6 100644 --- a/ruby/trema/topology/port.rb +++ b/ruby/trema/topology/port.rb @@ -62,7 +62,7 @@ def mac end - # @return [Array(Integer,Integer)] Port key 2-tuple for this Port instance + # @return [[Integer,Integer]] Port key 2-tuple for this Port instance def key return [dpid, portno] end @@ -72,7 +72,6 @@ def key # @param [Hash] port Hash containing Port properties. Must at least contain keys listed in Options. # @option port [Integer] :dpid Switch dpid which this port belongs # @option port [Integer] :portno port number - # @return [Port] # @example # port = Port.new( {:dpid => 1234, :portno => 42} ) def initialize( port ) @@ -115,10 +114,11 @@ def to_s def property_to_s - kvp_ary = @property.select { |key,_| not Port.is_mandatory_key?( key ) } - kvp_ary = kvp_ary.sort_by { |key,_| key.to_s } - s = kvp_ary.map { |key, val| "#{key.to_s}:#{val.inspect}" }.join(", ") - return s + kvp_ary = @property.select { |key,_| not Port.is_mandatory_key?( key ) }. + map { |key, val| [key.to_s, val] }. + sort { |lhs,rhs| lhs.first <=> rhs.first }. + map { |key, val| "#{key}:#{val.inspect}"} + return kvp_ary.join(", ") end end end diff --git a/ruby/trema/topology/switch.rb b/ruby/trema/topology/switch.rb index c6eb82288..fd6b7c3fe 100644 --- a/ruby/trema/topology/switch.rb +++ b/ruby/trema/topology/switch.rb @@ -57,10 +57,12 @@ def key # Add a port on this Switch. # @param [Port] port Port instance to add to switch + # @return self def add_port port raise TypeError, "Trema::Topology::Port expected" if not port.is_a?(Port) raise ArgumentError, "dpid mismatch. 0x#{ self.dpid.to_s(16) } expected but received: 0x#{ port.dpid.to_s(16) }" if ( self.dpid != port.dpid ) @ports[ port.portno ] = port + return self end @@ -68,10 +70,13 @@ def add_port port # It will be silently ignored if the port did not belong to this switch # @overload delete_port port # @param [Port] port Port instance to delete + # @return self # @overload delete_port port_hash # @param [Hash] port_hash a Hash with port info about instance to delete + # @return self # @overload delete_port portno # @param [Integer] portno portno of instance to delete + # @return self def delete_port port if port.is_a?(Port) @ports.delete( port.portno ) @@ -82,15 +87,18 @@ def delete_port port else raise TypeError, "Either Port, Port info(Hash), portno(Integer) expected" end + return self end # Add a link to this Switch. # It will be silently ignored if the link was not bound to this switch # @param [Link] link link to add. + # @return self def add_link link @links_in[ link.key ] = link if link.to_dpid == self.dpid @links_out[ link.key ] = link if link.from_dpid == self.dpid + return self end @@ -98,10 +106,13 @@ def add_link link # It will be silently ignored if the link was not bound to this switch # @overload delete_link link # @param [Port] port Link instance to delete + # @return self # @overload delete_link link_hash # @param [Hash] port_hash a Hash with link info about instance to delete + # @return self # @overload delete_link key # @param [[Integer, Integer, Integer, Integer]] key Link key 4-tuple of the link to delete + # @return self def delete_link link if link.is_a?(Link) key = link.key @@ -118,6 +129,7 @@ def delete_link link else raise TypeError, "Either Link, Link info(Hash), Link key tuple([Integer,Integer,Integer,Integer]) expected" end + return self end @@ -126,8 +138,7 @@ def delete_link link # @param [Hash] sw_hash a Hash containing Switch properties. Must at least contain keys listed in Options. # @option sw_hash [Integer] :dpid Switch dpid # @overload initialize dpid - # @param [Integer] dpid datapath_id - # @return Switch + # @param [Integer] dpid datapath_id # @example # sw = Switch.new( {:dpid => 0x1234} ) # sw = Switch.new( 0x1234 ) @@ -184,10 +195,11 @@ def to_s def property_to_s - kvp_ary = @property.select { |key,_| not Switch.is_mandatory_key?( key ) } - kvp_ary = kvp_ary.sort_by { |key,_| key.to_s } - s = kvp_ary.map { |key, val| "#{key.to_s}:#{val.inspect}" }.join(", ") - return s + kvp_ary = @property.select { |key,_| not Switch.is_mandatory_key?( key ) }. + map { |key, val| [key.to_s, val] }. + sort { |lhs,rhs| lhs.first <=> rhs.first }. + map { |key, val| "#{key}:#{val.inspect}"} + return kvp_ary.join(", ") end end end diff --git a/spec/trema/topology-map_spec.rb b/spec/trema/topology-map_spec.rb deleted file mode 100644 index 9119f8386..000000000 --- a/spec/trema/topology-map_spec.rb +++ /dev/null @@ -1,459 +0,0 @@ -# -# Copyright (C) 2008-2013 NEC Corporation -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License, version 2, as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - - -require File.join( File.dirname( __FILE__ ), "..", "spec_helper" ) -require "trema" -require "trema/topology/map_api" - - -include Trema::TopologyMap - -describe Trema::TopologyMap, :nosudo => true do - - describe "Monkey patched Switch" do - it "should have method key_str" do - s = Switch.new( { :dpid => 0x1234, :magic => 42 } ) - expect( s.key_str ).to match("1234") - end - - it "up state should be overwritable" do - s = Switch.new( { :dpid => 0x1234, :up => true } ) - s.up = false - expect( s.up? ).to be_false - - s = Switch.new( { :dpid => 0x1234, :up => false } ) - s.up = true - expect( s.up? ).to be_true - end - - describe "[],[]=" do - before do - @s = Switch.new( {:dpid => 0x1234, :extra => true } ) - end - - it "should have access to it's property" do - expect( @s[:extra] ).to be_true - end - - it "should be writable to non mandatory key" do - @s[:extra] = false - expect( @s[:extra] ).to be_false - end - - it "should raise exception if attempt to write to non mandatory key" do - expect { - @s[:dpid] = 0x5678 - }.to raise_error(ArgumentError) - end - end - - describe "delete" do - before do - @s = Switch.new( {:dpid => 0x1234, :extra => true } ) - end - - it "should remove property value" do - @s.delete(:extra) - expect( @s.property.has_key?(:extra) ).to be_false - end - - it "should ignore mandatory key" do - @s.delete(:dpid) - expect( @s[:dpid] ).to eq(0x1234) - end - end - - describe "update" do - before do - @s = Switch.new( {:dpid => 0x1234, :extra => true } ) - end - it "should update property by given hash" do - @s.update( {:extra => false, :new => 42}) - expect( @s[:extra] ).to be_false - expect( @s[:new] ).to eq(42) - end - - it "should ignore update to mandatory key" do - @s.update( {:extra => false, :dpid => 42}) - expect( @s[:extra] ).to be_false - expect( @s[:dpid] ).to eq(0x1234) - end - end - end - - describe "Monkey Patched Port" do - it "should have method key_str" do - port = Port.new( {:dpid => 0x1234, :portno => 42 } ) - expect( port.key_str ).to match("1234-42") - end - end - - describe "Monkey Patched Link" do - it "should have method key_str" do - link = Link.new( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72 } ) - expect( link.key_str ).to match("1234-42-5678-72") - end - - describe "[],[]=" do - it "should have access to it's property" do - link = Link.new( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72, :extra => true } ) - expect( link[:extra] ).to be_true - end - - it "should be writable to non mandatory key" do - link = Link.new( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72, :extra => true } ) - link[:extra] = false - expect( link[:extra] ).to be_false - end - - it "should raise exception if attempt to write to non mandatory key" do - link = Link.new( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72 } ) - expect { - link[:from_portno] = 55 - }.to raise_error(ArgumentError) - end - end - - describe "delete" do - it "should remove property value" do - link = Link.new( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72, :extra => true } ) - link.delete(:extra) - expect( link.property.has_key?(:extra) ).to be_false - end - - it "should ignore mandatory key" do - link = Link.new( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72, :extra => true } ) - link.delete(:from_dpid) - expect( link[:from_dpid] ).to eq(0x1234) - end - end - - describe "update" do - it "should update property by given hash" do - link = Link.new( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72, :extra => true } ) - link.update( {:extra => false, :new => 42}) - expect( link[:extra] ).to be_false - expect( link[:new] ).to eq(42) - end - - it "should ignore update to mandatory key" do - link = Link.new( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72, :extra => true } ) - link.update( {:extra => false, :from_dpid => 42}) - expect( link[:extra] ).to be_false - expect( link[:from_dpid] ).to eq(0x1234) - end - end - end - - describe Map do - it "should initialize it's members to empty" do - c = Map.new - expect( c.switches.empty? ).to be_true - expect( c.links.empty? ).to be_true - end - - it "should have switch manipulation methods" do - c = Map.new - c.add_switch Switch.new( {:dpid => 0x1234 } ) - expect( c.switches.empty? ).to be_false - - expect( c.lookup_switch_by_dpid( 0x1234 ) ).not_to be_nil - - c.add_switch Switch.new( {:dpid => 0x5678 } ) - expect( c.switches[ 0x5678 ] ).not_to be_nil - - c.del_switch Switch.new( {:dpid => 0x5678 } ) - c.del_switch_by_dpid 0x1234 - expect( c.switches.empty? ).to be_true - end - - describe "link manipulation methods" do - before do - @c = Map.new - @c.add_switch Switch.new( {:dpid => 0x1234 } ) - @c.add_switch Switch.new( {:dpid => 0x5678 } ) - end - - it "should have add_link" do - @c.add_link Link.new( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72 } ) - expect( @c.links.empty? ).to be_false - end - - it "should have lookup_link_by_hash" do - @c.add_link Link.new( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72 } ) - expect( @c.lookup_link_by_hash( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72 } ) ).not_to be_nil - end - - it "should have del_link(Link)" do - @c.add_link Link.new( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72 } ) - @c.del_link Link.new( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72 } ) - expect( @c.lookup_link_by_hash( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72 } ) ).to be_nil - end - - it "should have del_link(Hash)" do - @c.add_link Link.new( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72 } ) - @c.del_link( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72 } ) - expect( @c.lookup_link_by_hash( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72 } ) ).to be_nil - end - - it "should have del_link_by_key" do - @c.add_link Link.new( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72 } ) - @c.del_link_by_key [0x1234, 42, 0x5678, 72] - expect( @c.lookup_link_by_hash( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72 } ) ).to be_nil - end - end - - describe "Switch events" do - it "should add Switch on switch up event" do - c = Map.new - c.update_switch( {:dpid => 0x1234, :up => true } ) - expect( c.switches ).to include(0x1234) - - expect( c.switches[ 0x1234 ].up? ).to be_true - expect( c.switches[ 0x1234 ][:up] ).to be_true - end - - it "should delete Switch on switch up event" do - c = Map.new - c.add_switch Switch.new( {:dpid => 0x1234, :up => true } ) - c.add_switch Switch.new( {:dpid => 0x5678, :up => true } ) - - c.update_switch( {:dpid => 0x1234, :up => false } ) - expect( c.switches ).not_to include(0x1234) - expect( c.switches ).to include(0x5678) - end - - it "should update Switch on existing switch's up event" do - c = Map.new - c.add_switch Switch.new( {:dpid => 0x1234, :up => true, :extra => "Old Value", :no_change => true } ) - c.update_switch( {:dpid => 0x1234, :up => true, :extra => "New Value" } ) - expect( c.switches ).to include(0x1234) - expect( c.switches[ 0x1234 ].up? ).to be_true - expect( c.switches[ 0x1234 ][:up] ).to be_true - expect( c.switches[ 0x1234 ][:extra] ).to match("New Value") - expect( c.switches[ 0x1234 ][:no_change] ).to be_true - end - end - - describe "Link event" do - before do - @c = Map.new - @c.add_switch Switch.new( {:dpid => 0x1234, :up => true } ) - @c.add_switch Switch.new( {:dpid => 0x5678, :up => true } ) - end - - it "should add Link on link up event" do - @c.update_link( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72, :up => true, :unstable => false } ) - expect( @c.links[ [0x1234, 42, 0x5678, 72] ] ).not_to be_nil - expect( @c.links[ [0x1234, 42, 0x5678, 72] ].up? ).to be_true - expect( @c.links[ [0x1234, 42, 0x5678, 72] ].unstable? ).to be_false - expect( @c.links[ [0x1234, 42, 0x5678, 72] ][:up] ).to be_true - expect( @c.links[ [0x1234, 42, 0x5678, 72] ][:unstable] ).to be_false - - expect( @c.switches[0x1234].links_out[ [0x1234, 42, 0x5678, 72] ] ).not_to be_nil - expect( @c.switches[0x5678].links_in[ [0x1234, 42, 0x5678, 72] ] ).not_to be_nil - end - - it "should remove Link on link down event" do - @c.add_link Link.new( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72 } ) - expect( @c.links.empty? ).to be_false - - @c.update_link( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72, :up => false, :unstable => false } ) - expect( @c.links[ [0x1234, 42, 0x5678, 72] ] ).to be_nil - end - - it "should update Link on existing link event" do - @c.update_link( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72, :up => true, :unstable => false } ) - - @c.update_link( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72, :up => true, :unstable => true } ) - expect( @c.links[ [0x1234, 42, 0x5678, 72] ] ).not_to be_nil - expect( @c.links[ [0x1234, 42, 0x5678, 72] ].up? ).to be_true - expect( @c.links[ [0x1234, 42, 0x5678, 72] ].unstable? ).to be_true - expect( @c.links[ [0x1234, 42, 0x5678, 72] ][:up] ).to be_true - expect( @c.links[ [0x1234, 42, 0x5678, 72] ][:unstable] ).to be_true - - expect( @c.switches[0x1234].links_out[ [0x1234, 42, 0x5678, 72] ] ).not_to be_nil - expect( @c.switches[0x5678].links_in[ [0x1234, 42, 0x5678, 72] ] ).not_to be_nil - end - end - - describe "Link event recoverable error" do - before do - @c = Map.new - end - it "should add Switch and Link on link up event, when switch did not exist" do - @c.update_link( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72, :up => true, :unstable => false } ) - expect( @c.links[ [0x1234, 42, 0x5678, 72] ] ).not_to be_nil - expect( @c.links[ [0x1234, 42, 0x5678, 72] ].up? ).to be_true - expect( @c.links[ [0x1234, 42, 0x5678, 72] ].unstable? ).to be_false - expect( @c.links[ [0x1234, 42, 0x5678, 72] ][:up] ).to be_true - expect( @c.links[ [0x1234, 42, 0x5678, 72] ][:unstable] ).to be_false - - expect( @c.switches[0x1234].links_out[ [0x1234, 42, 0x5678, 72] ] ).not_to be_nil - expect( @c.switches[0x5678].links_in[ [0x1234, 42, 0x5678, 72] ] ).not_to be_nil - end - end - - describe "Port event" do - before do - @c = Map.new - @c.add_switch Switch.new( {:dpid => 0x1234, :up => true } ) - @c.add_switch Switch.new( {:dpid => 0x5678, :up => true } ) - end - - it "should add Port on port up event" do - @c.update_port( { :dpid => 0x1234, :portno => 42, :name => "Port Name", :mac => "FF:FF:FF:FF:FF:FF", :external => false, :up => true } ) - expect( @c.switches[0x1234].ports[42].up? ).to be_true - expect( @c.switches[0x1234].ports[42].external? ).to be_false - expect( @c.switches[0x1234].ports[42].name ).to eq("Port Name") - expect( @c.switches[0x1234].ports[42].mac ).to eq("FF:FF:FF:FF:FF:FF") - end - - it "should delete Port on port down event" do - @c.update_port( { :dpid => 0x1234, :portno => 42, :name => "Port Name", :mac => "FF:FF:FF:FF:FF:FF", :external => false, :up => false } ) - expect( @c.switches[0x1234].ports ).not_to include(42) - end - - it "should update Port on existing port up event" do - @c.update_port( { :dpid => 0x1234, :portno => 42, :name => "Port Name", :mac => "FF:FF:FF:FF:FF:FF", :external => false, :up => true } ) - @c.update_port( { :dpid => 0x1234, :portno => 42, :name => "New Port Name", :external => false, :up => true } ) - expect( @c.switches[0x1234].ports[42].up? ).to be_true - expect( @c.switches[0x1234].ports[42].external? ).to be_false - expect( @c.switches[0x1234].ports[42].name ).to eq("New Port Name") - expect( @c.switches[0x1234].ports[42].mac ).to eq("FF:FF:FF:FF:FF:FF") - end - end - - describe "Port event recoverable error" do - before do - @c = Map.new - end - it "should add Switch and Port on port up event, when switch did not exist" do - @c.update_port( { :dpid => 0x1234, :portno => 42, :name => "Port Name", :mac => "FF:FF:FF:FF:FF:FF", :external => false, :up => true } ) - expect( @c.switches[0x1234].ports[42].up? ).to be_true - expect( @c.switches[0x1234].ports[42].external? ).to be_false - expect( @c.switches[0x1234].ports[42].name ).to eq("Port Name") - expect( @c.switches[0x1234].ports[42].mac ).to eq("FF:FF:FF:FF:FF:FF") - end - end - - it "should be serializable to human readable form by to_s" do - c = Map.new - - s = Switch.new( { :dpid => 0x1234, :up => true, :magic => 42 } ) - s.add_port Port.new( { :dpid => 0x1234, :portno => 42, :up => true, :not_used => 1 } ) - c.add_switch s - c.add_link Link.new( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72, :not_used => 1, :up => true } ) - c.add_link Link.new( {:from_dpid => 0xABCD, :from_portno => 102, :to_dpid => 0x1234, :to_portno => 42, :not_used => 1, :up => true } ) - expect( c.to_s ).to be == <<-EOS -Map: -Switch: 0x1234 - {magic:42, up:true} - Port: 0x1234:42 - {not_used:1, up:true} - Links_in - <= 0xabcd:102 - Links_out - => 0x5678:72 -Switch: 0xabcd - {up:true} - Links_out - => 0x1234:42 -Switch: 0x5678 - {up:true} - Links_in - <= 0x1234:42 -Link: (0xabcd:102)->(0x1234:42) - {not_used:1, up:true} -Link: (0x1234:42)->(0x5678:72) - {not_used:1, up:true} - EOS - end - end - - describe "Map management functions" do - before do - class DummyController - include Topology - end - @c = DummyController.new - end - - it "should respond to get_last_map" do - expect( @c ).to respond_to( :get_last_map ) - end - - it "should respond to get_map" do - expect( @c ).to respond_to( :get_map ) - end - - it "should respond to map_ready?" do - expect( @c ).to respond_to( :map_ready? ) - end - - it "should respond to map_up_to_date?" do - expect( @c ).to respond_to( :map_up_to_date? ) - end - - it "should issue all status request on rebuild request" do - - expect( @c.map_up_to_date? ).to be_false - - @c.should_receive(:get_all_switch_status).and_yield( [ {:dpid => 0x1234, :up => true } ] ) - @c.should_receive(:get_all_port_status).and_yield( [ {:dpid => 0x1234, :portno => 42, :up => true } ] ) - @c.should_receive(:get_all_link_status).and_yield( [ {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72, :up => true } ] ) - - @c.should_receive(:map_ready).once - - @c.send_rebuild_map_request - - expect( @c.map_up_to_date? ).to be_true - end - - it "should update Map on Switch update event" do - @c.update_map_by_switch_hash( {:dpid => 0x1234, :up => true } ) - map = @c.get_last_map - expect( map.lookup_switch_by_dpid( 0x1234 ) ).not_to be_nil - - @c.update_map_by_switch_hash( {:dpid => 0x1234, :up => false } ) - map = @c.get_last_map - expect( map.lookup_switch_by_dpid( 0x1234 ) ).to be_nil - end - - it "should update Map on Port update event" do - @c.update_map_by_port_hash( {:dpid => 0x1234, :portno => 42, :up => true } ) - map = @c.get_last_map - expect( map.lookup_switch_by_dpid( 0x1234 ).ports[42] ).not_to be_nil - - @c.update_map_by_port_hash( {:dpid => 0x1234, :portno => 42, :up => false } ) - map = @c.get_last_map - expect( map.lookup_switch_by_dpid( 0x1234 ).ports[42] ).to be_nil - end - - it "should update Map on Link update event" do - @c.update_map_by_link_hash( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72, :up => true } ) - map = @c.get_last_map - expect( map.lookup_link_by_hash( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72 } ) ).not_to be_nil - - @c.update_map_by_link_hash( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72, :up => false } ) - map = @c.get_last_map - expect( map.lookup_link_by_hash( {:from_dpid => 0x1234, :from_portno => 42, :to_dpid => 0x5678, :to_portno => 72 } ) ).to be_nil - end - - end -end - -### Local variables: -### mode: Ruby -### coding: utf-8-unix -### indent-tabs-mode: nil -### End: diff --git a/spec/trema/topology-switch_spec.rb b/spec/trema/topology-switch_spec.rb index 08c4e4d55..a9c75d134 100644 --- a/spec/trema/topology-switch_spec.rb +++ b/spec/trema/topology-switch_spec.rb @@ -78,14 +78,8 @@ s.add_port Port.new( { :dpid => 0x1234, :portno => 1 } ) expect( s.ports[1] ).not_to be_nil - s.update_port( { :dpid => 0x1234, :portno => 3, :up => true } ) - expect( s.ports[3] ).not_to be_nil - s.delete_port Port.new( { :dpid => 0x1234, :portno => 2 } ) expect( s.ports[2] ).to be_nil - - s.update_port( { :dpid => 0x1234, :portno => 1, :up => false } ) - expect( s.ports[1] ).to be_nil end it "should have link manipulation methods" do diff --git a/src/examples/topology/README.md b/src/examples/topology/README.md index 0e85a198d..14e0efc56 100644 --- a/src/examples/topology/README.md +++ b/src/examples/topology/README.md @@ -67,57 +67,6 @@ How to run (show_topology/show_switch_status) $ ./trema run src/examples/topology/show-switch_status.rb -How to run (change-history.rb) ------------------------------- - -1. Change to trema directory and build trema, if you haven't done so already. - - $ cd $TREMA_HOME - $ ./build.rb - -2. Start topology daemon. - - $ ./trema run -c src/examples/topology/change-history.conf & - -3. Run change-history.rb from trema run - - $ ./trema run src/examples/topology/change-history.rb - -4. Controller will output will be written to change-history.dot in current directory. - - (In another terminal) - $ tail -F change-history.dot - digraph { - subgraph cluster0 { - graph [label="Gen 0\ninitial"]; - "0x1_0" [label="0x1", color="green"]; - "0x2_0" [label="0x2", color="green"]; - "0x3_0" [label="0x3", color="green"]; - "0x1_0" -> "0x2_0" [color="green"]; - "0x1_0" -> "0x3_0" [color="green"]; - "0x2_0" -> "0x3_0" [color="green"]; - "0x3_0" -> "0x2_0" [color="green"]; - "0x3_0" -> "0x1_0" [color="green"]; - "0x2_0" -> "0x1_0" [color="green"]; - -5. See how topology change is notified to dot file. - Example: stop switch with dpid 0x3 - - (In another terminal) - $ ./trema kill 0x3 - - Example: restart switch with dpid 0x3 - - $ ./trema up 0x3 - -6. Graphically see topology change history. - - $ dot -Tsvg -O change-history.dot - $ firefox change-history.dot.svg - - You may need to install graphviz package to use dot command. - - $ sudo apt-get install graphviz License & Terms --------------- diff --git a/src/examples/topology/change-history.conf b/src/examples/topology/change-history.conf deleted file mode 100644 index e838c90f1..000000000 --- a/src/examples/topology/change-history.conf +++ /dev/null @@ -1,20 +0,0 @@ - -1.upto( 3 ).each do | sw | - vswitch { dpid sw } - 1.upto( sw - 1 ).each do | peer | - link "%#x" % sw, "%#x" % peer - end -end - -run { - path "./objects/topology/topology" -# options "-l debug" -} - -run { - path "./objects/examples/dumper/dumper" -} - -event :port_status => "topology", :packet_in => "filter", :state_notify => "topology" -filter :lldp => "topology", :packet_in => "dumper" - diff --git a/src/examples/topology/change-history.rb b/src/examples/topology/change-history.rb deleted file mode 100644 index a1eea5d98..000000000 --- a/src/examples/topology/change-history.rb +++ /dev/null @@ -1,187 +0,0 @@ -# -# Copyright (C) 2008-2013 NEC Corporation -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License, version 2, as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -require "trema/topology/map_api" - - -# Controller which keeps printing Switch and Link change event as graphviz dot file. -# It will shutdown itself if there was no Switch or Link event for 60 seconds. -# -class ChangeHistory < Controller - include TopologyMap - - # TODO Add option to specify idle check interval. - periodic_timer_event :shutdown_on_idle, 60 - - oneshot_timer_event :on_start, 0 - def on_start - send_enable_topology_discovery - send_rebuild_map_request - - # TODO Add option to specify file from command line. - @dotfile = File.open( "change-history.dot", "w" ) - - # digraph { - puts_dotfile "digraph {" - at_exit do - # } for digraph - puts_dotfile "}" - end - end - - - def map_ready g - @generation = 0 - - # Initially color everything as updated (green) - g.switches.each do | _, sw | - sw[:color] = "green" - end - g.links.each do | _, lnk | - lnk[:color] = "green" - end - - puts_dotfile to_dot_subgraph( g, @generation, "initial" ) - end - - - def switch_status_up dpid - if not instance_variable_defined?(:@generation) then - return nil - end - - @generation += 1 - g = get_last_map - remove_old_colors( g, @generation ) - - sw = Switch.new( dpid ) - sw[:generation] = @generation - sw[:color] = "green" - update_map_by_switch_hash sw - sublabel = "0x#{dpid.to_s(16)} up" - puts_dotfile to_dot_subgraph( g, @generation, sublabel ) - end - - - def switch_status_down dpid - if not instance_variable_defined?(:@generation) then - return nil - end - - @generation += 1 - g = get_last_map - remove_old_colors( g, @generation ) - - sw = Switch.new( dpid ) - sw.up = false - sw[:generation] = @generation - g.switches[ dpid ][:color] = "red" if g.switches[ dpid ] - sublabel = "0x#{dpid.to_s(16)} down" - puts_dotfile to_dot_subgraph( g, @generation, sublabel ) - # update to new state. (switch instance will be removed) - update_map_by_switch_hash sw - end - - - def link_status_updated link_attr - if not instance_variable_defined?(:@generation) then - return nil - end - link = Link.new( link_attr ) - - @generation += 1 - link[:generation] = @generation - - g = get_last_map - remove_old_colors( g, @generation ) - - if link.up? then - link[:color] = "green" - update_map_by_link_hash link - sublabel = "(0x#{link.from_dpid.to_s(16)} -> 0x#{link.to_dpid.to_s(16)}) up" - puts_dotfile to_dot_subgraph( g, @generation, sublabel ) - else - g.links[ link.key ][:color] = "red" if g.links[ link.key ] - sublabel = "(0x#{link.from_dpid.to_s(16)} -> 0x#{link.to_dpid.to_s(16)}) down" - puts_dotfile to_dot_subgraph( g, @generation, sublabel ) - # update to new state. (link instance will be removed) - update_map_by_link_hash link - end - end - - - def shutdown_on_idle - @last_generation ||= -1 - if @last_generation == @generation then - shutdown! - else - @last_generation = @generation - end - end - - - def remove_old_colors( g, generation ) - g.switches.each do | _, sw | - sw[:generation] ||= 0 - sw.delete(:color) if sw[:generation] != generation - end - g.links.each do | _, lnk | - lnk[:generation] ||= 0 - lnk.delete(:color) if lnk[:generation] != generation - end - end - - - def to_dot_subgraph( g, generation, sublabel="" ) - s = " subgraph cluster#{generation} {\n" - s << %Q( graph [label="Gen #{generation}\\n#{sublabel}"];\n) - # // switches - g.switches.each do | _, sw | - dot_attrs = %Q(label="0x#{sw.dpid.to_s(16)}") - if sw.property.has_key?(:color) then - dot_attrs << ", " unless dot_attrs.empty? - dot_attrs << %Q(color="#{sw[:color]}") - end - s << %Q( "0x#{sw.dpid.to_s(16)}_#{generation}" [#{dot_attrs}];\n) - end - # // links - g.links.each do | _, lnk | - dot_attrs = "" - if lnk.property.has_key?(:color) then - dot_attrs << ", " unless dot_attrs.empty? - dot_attrs << %Q(color="#{lnk[:color]}") - end - s << %Q( "0x#{lnk.from_dpid.to_s(16)}_#{generation}" -> "0x#{lnk.to_dpid.to_s(16)}_#{generation}" [#{dot_attrs}];\n) - end - # } for subgraph - s << " }\n" - return s - end - - - def puts_dotfile s - begin - @dotfile.puts s - @dotfile.flush - rescue - error "Failed to write dotfile (#{$!.inspect})\n\t#{$!.backtrace.join("\n\t") }" - STDERR.puts "Failed to write dotfile." - shutdown! - end - end -end -