diff --git a/Rantfile b/Rantfile index 53d1f4b51..38aea0991 100644 --- a/Rantfile +++ b/Rantfile @@ -573,6 +573,12 @@ gen DirectedRule, topology_examples_objects_dir => [ topology_examples_source_di sys "gcc -I#{ Trema.include } -I#{ Trema.openflow } #{ var :CFLAGS } -c -o #{ t.name } #{ t.source }" end +enable_discovery_objects = [ + "enable_discovery.o", +].collect do | each | + File.join topology_examples_objects_dir, each +end + show_topology_objects = [ "show_topology.o", ].collect do | each | @@ -586,6 +592,7 @@ show_switch_status_objects = [ end topology_examples_binaries = { + "enable_discovery" => enable_discovery_objects, "show_topology" => show_topology_objects, "show_switch_status" => show_switch_status_objects, } diff --git a/features/examples/topology.show_switch_status.feature b/features/examples/topology.show_switch_status.feature index 26b267af0..b1ae61122 100644 --- a/features/examples/topology.show_switch_status.feature +++ b/features/examples/topology.show_switch_status.feature @@ -5,9 +5,13 @@ Feature: show_switch_status example. show_switch_status command will query for all the switch and port information that the topology daemon hold and print them to standard output. + Background: + Given I cd to "../../src/examples/topology/" + @slow_process Scenario: [C API] Show switch and port information obtained from topology. - Given a file named "topology.conf" with: + Given I compile "show_switch_status.c" into "show_switch_status" + Given a file named "show_switch_status.conf" with: """ vswitch("topology1") { datapath_id "0xe0" } vhost ("host1") { @@ -24,23 +28,11 @@ Feature: show_switch_status example. link "topology1", "host1" link "topology1", "host2" - - run { - path "../../objects/topology/topology" - options "--always_run_discovery" - } - - run { - path "../../objects/examples/dumper/dumper" - } - - event :port_status => "topology", :packet_in => "filter", :state_notify => "topology" - filter :lldp => "topology", :packet_in => "dumper" """ - And I run `trema run -c topology.conf -d` - And wait until "topology" is up - And *** sleep 16 *** - When I run `trema run ../../objects/examples/topology/show_switch_status` + And I run `trema run ../repeater_hub/repeater-hub.rb -c show_switch_status.conf -d` + And I run `trema run "../../../objects/topology/topology -d"` + And *** sleep 4 *** + When I run `trema run ./show_switch_status` Then the output should contain: """ Switch status @@ -50,5 +42,5 @@ Feature: show_switch_status example. """ Port status """ - And the output should match /dpid : 0xe0, port : 1\(.+\), status : up, external : true/ - And the output should match /dpid : 0xe0, port : 2\(.+\), status : up, external : true/ + And the output should match / dpid : 0xe0, port : 1\(.+\), status : up, external : (true|false)/ + And the output should match / dpid : 0xe0, port : 2\(.+\), status : up, external : (true|false)/ diff --git a/features/examples/topology.show_topology.feature b/features/examples/topology.show_topology.feature index 17b29e63d..4791bbb29 100644 --- a/features/examples/topology.show_topology.feature +++ b/features/examples/topology.show_topology.feature @@ -1,13 +1,26 @@ -Feature: show topology - - As a Trema user - I want to control multiple openflow switches using topology application - So that I can show topology +Feature: show_topology example. + + show_topology is a simple usage example of topology C API. + + show_topology command will query for all the link information that + the topology daemon hold and print them in trema network DSL style. + + Topology daemon's link discovery feature must be enabled prior to running + show_topology command in order to obtain non-empty result. + + Topology daemon's link discovery feature can be enabled + by specifing "--always_run_discovery" option when starting topology daemon, + or by calling enable_topology_discovery() API from client application. + + Background: + Given I cd to "../../src/examples/topology/" @slow_process - Scenario: Four openflow switches, four servers - Given a file named "topology.conf" with: + Scenario: [C API] Show discovered link topology. (Directly start topology daemon) + Given I compile "show_topology.c" into "show_topology" + Given I compile "enable_discovery.c" into "enable_discovery" + Given a file named "show_topology.conf" with: """ vswitch("topology1") { datapath_id "0x1" } vswitch("topology2") { datapath_id "0x2" } @@ -20,23 +33,82 @@ Feature: show topology link "topology2", "topology3" link "topology2", "topology4" link "topology3", "topology4" - - run { - path "../../objects/topology/topology" - options "--always_run_discovery" + """ + And I run `trema run ../repeater_hub/repeater-hub.rb -c show_topology.conf -d` + And I run `trema run "../../../objects/topology/topology -d --always_run_discovery"` + And *** sleep 4 *** + When I run `trema run ./show_topology` + Then the output should contain: + """ + vswitch { + datapath_id "0x2" } - - run { - path "../../objects/examples/dumper/dumper" + """ + And the output should contain: + """ + vswitch { + datapath_id "0x3" } + """ + And the output should contain: + """ + vswitch { + datapath_id "0x1" + } + """ + And the output should contain: + """ + vswitch { + datapath_id "0x4" + } + """ + And the output should contain: + """ + link "0x2", "0x1" + """ + And the output should contain: + """ + link "0x3", "0x2" + """ + And the output should contain: + """ + link "0x3", "0x1" + """ + And the output should contain: + """ + link "0x4", "0x2" + """ + And the output should contain: + """ + link "0x4", "0x3" + """ + And the output should contain: + """ + link "0x4", "0x1" + """ + + @slow_process + Scenario: [C API] Show discovered link topology. (Enable link discovery by API) + Given I compile "show_topology.c" into "show_topology" + Given I compile "enable_discovery.c" into "enable_discovery" + Given a file named "show_topology.conf" with: + """ + vswitch("topology1") { datapath_id "0x1" } + vswitch("topology2") { datapath_id "0x2" } + vswitch("topology3") { datapath_id "0x3" } + vswitch("topology4") { datapath_id "0x4" } - event :port_status => "topology", :packet_in => "filter", :state_notify => "topology" - filter :lldp => "topology", :packet_in => "dumper" + link "topology1", "topology2" + link "topology1", "topology3" + link "topology1", "topology4" + link "topology2", "topology3" + link "topology2", "topology4" + link "topology3", "topology4" """ - And I run `trema run -c topology.conf -d` - And wait until "topology" is up - And *** sleep 4 *** - And I run `trema run ../../objects/examples/topology/show_topology` + And I run `trema run ../repeater_hub/repeater-hub.rb -c show_topology.conf -d` + And I run `trema run "./enable_discovery"` + And *** sleep 4 *** + When I run `trema run ./show_topology` Then the output should contain: """ vswitch { @@ -85,5 +157,3 @@ Feature: show topology """ link "0x4", "0x1" """ - - diff --git a/src/examples/topology/.gitignore b/src/examples/topology/.gitignore new file mode 100644 index 000000000..585d6b5ef --- /dev/null +++ b/src/examples/topology/.gitignore @@ -0,0 +1,6 @@ +enable_discovery +show_topology +show_topology.conf +show_switch_status +show_switch_status.conf + diff --git a/src/examples/topology/README.md b/src/examples/topology/README.md index bb4c8ef1c..94a9e6c04 100644 --- a/src/examples/topology/README.md +++ b/src/examples/topology/README.md @@ -9,14 +9,17 @@ This directory includes sample application using libtopology. - `show_swith_status` is a command line application, which retrieves switch and port information from Topology daemon and print them to stdout. +- `enable_discovery` is a command line application, which enables + link discovery feature using the topology API. + +----------+ +-----------+ +------+ +------------------+ | switch | * 1 | packet in | 1 * |dumper| |show_topology/ | | daemon | --------> | filter | --------> | | |show_switch_status| +----------+ packet in +-----------+ packet in +------+ +------------------+ ^ 1 ^ | * ^ - | | | libtopology | - | `-------. | | + | | | | + | `-------. | topology API | v 1 | | 1 v +----------+ | | packet in(LLDP) +-----------+ | openflow | | `------------------------->| topology | @@ -32,19 +35,31 @@ How to run $ cd $TREMA_HOME $ ./build.rb -2. Start topology daemon +2. Start network to discover + + $ ./trema run ./objects/examples/dumper/dumper -c src/examples/topology/topology_fullmesh.conf & + + # dumper is used in the above example, only because trema cannot run without + # any controller specified. + +3. Enable link discovery feature + by adding "--always_run_discovery" option when starting topology + + $ ./trema run ./objects/examples/topology/topology -d --always_run_discovery + + Or by using API call. - $ ./trema run -c src/examples/topology/topology_fullmesh.conf & + $ ./trema run ./objects/examples/topology/enable_discovery -3. Run show_topology/show_switch_status from trema run +4. Run show_topology/show_switch_status from trema run $ ./trema run objects/examples/topology/show_topology $ ./trema run objects/examples/topology/show_switch_status Or invoke each commands directly - $ env TREMA_HOME=`pwd` objects/examples/topology/show_topology - $ env TREMA_HOME=`pwd` objects/examples/topology/show_switch_status + $ env TREMA_HOME=`pwd` src/examples/topology/show_topology + $ env TREMA_HOME=`pwd` src/examples/topology/show_switch_status License & Terms diff --git a/src/examples/topology/enable_discovery.c b/src/examples/topology/enable_discovery.c new file mode 100644 index 000000000..464f605d0 --- /dev/null +++ b/src/examples/topology/enable_discovery.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2012 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. + */ + + +#include +#include +#include +#include +#include "trema.h" + + +void +usage() { + topology_service_interface_usage( get_executable_name(), "Show switch status", NULL ); +} + + +static void +timed_out( void *user_data ) { + UNUSED( user_data ); + + error( "timed out." ); + + stop_trema(); +} + + +static void +result_response( void *user_data, const topology_response *res ) { + UNUSED( user_data ); + if ( res->status != TD_RESPONSE_OK ) { + error( "Enable topology discovery failed" ); + } else { + info( "Enabled topology discovery" ); + } + stop_trema(); +} + +int +main( int argc, char *argv[] ) { + init_trema( &argc, &argv ); + init_topology_service_interface_options( &argc, &argv ); + init_libtopology( get_topology_service_interface_name() ); + + enable_topology_discovery( result_response, NULL ); + + add_periodic_event_callback( 15, timed_out, NULL ); + + start_trema(); + + finalize_libtopology(); + finalize_topology_service_interface_options(); + + return 0; +} + + +/* + * Local variables: + * c-basic-offset: 2 + * indent-tabs-mode: nil + * End: + */ diff --git a/src/examples/topology/topology.conf b/src/examples/topology/topology.conf index 361183a1c..40bc482f9 100644 --- a/src/examples/topology/topology.conf +++ b/src/examples/topology/topology.conf @@ -17,15 +17,3 @@ vhost ("host2") { link "0xe0", "host1" link "0xe0", "host2" - -run { - path "./objects/topology/topology" - options "--always_run_discovery" -} - -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/topology_fullmesh.conf b/src/examples/topology/topology_fullmesh.conf index eda464f8a..17d4bed98 100644 --- a/src/examples/topology/topology_fullmesh.conf +++ b/src/examples/topology/topology_fullmesh.conf @@ -48,15 +48,3 @@ link "0xe0", "0xe3" link "0xe1", "0xe2" link "0xe1", "0xe3" link "0xe2", "0xe3" - -run { - path "./objects/topology/topology" - options "--always_run_discovery" -} - -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/lib/topology.c b/src/lib/topology.c index 92f86f344..76e4326c5 100644 --- a/src/lib/topology.c +++ b/src/lib/topology.c @@ -25,6 +25,10 @@ #include #include #include +#include +#include +#include +#include #include "trema.h" #include "topology.h" @@ -536,6 +540,86 @@ check_transaction_table( void *user_data ) { } +static const char TOPOLOGY_DAEMON_PATH[] = "objects/topology/topology"; + +static void +maybe_start_topology_daemon( const char* service_name ) { + // topology daemon process check + if ( get_trema_process_from_name( service_name ) > 0 ) { + return; + } + // topology daemon not running + + pid_t pid = fork(); + if ( pid < 0 ) { + error( "Failed to fork. %s.", strerror( errno ) ); + return; + } + + if ( pid == 0 ) { + // child process + + // close all open fd + DIR* dir = opendir( "/proc/self/fd" ); + if ( dir == NULL ) { + error( "Failed to opendir. %s.", strerror( errno ) ); + } else { + struct dirent *entry; + while ( ( entry = readdir( dir ) ) != NULL ) { + int openfd = atoi( entry->d_name ); + close( openfd ); + } + closedir( dir ); + dir = NULL; + } + + // exec + int in_fd = open( "/dev/null", O_RDONLY ); + if ( in_fd != 0 ) { + dup2( in_fd, 0 ); + close( in_fd ); + } + int out_fd = open( "/dev/null", O_WRONLY ); + if ( out_fd != 1 ) { + dup2( out_fd, 1 ); + close( out_fd ); + } + int err_fd = open( "/dev/null", O_WRONLY ); + if ( err_fd != 2 ) { + dup2( err_fd, 2 ); + close( err_fd ); + } + char* daemon_path = xasprintf( "%s/%s", get_trema_home(), + TOPOLOGY_DAEMON_PATH ); + char arg_daemonize[ ] = "-d"; + char arg_name[ ] = "-n"; + char* arg_name_opt = xstrdup( service_name ); + + char* argv[ 4 ]; + argv[ 0 ] = daemon_path; + argv[ 1 ] = arg_daemonize; + argv[ 2 ] = arg_name; + argv[ 3 ] = arg_name_opt; + + execvp( daemon_path, argv ); + int err = errno; + error( "Failed to execvp: %s %s %s %s. %s.", argv[ 0 ], argv[ 1 ], + argv[ 2 ], argv[ 3 ], strerror( err ) ); + +// xfree( daemon_path ); +// xfree( arg_name_opt ); + UNREACHABLE(); + } else { + // parent process + // wait for topology daemon to start + int try = 0; + const int max_try = 5; + while ( get_trema_process_from_name( service_name ) == -1 && ++try <= max_try ) { + sleep( 1 ); + } + } +} + bool init_libtopology( const char *service_name ) { if ( topology_name != NULL || libtopology_queue_name != NULL ) { @@ -543,6 +627,8 @@ init_libtopology( const char *service_name ) { return false; } + maybe_start_topology_daemon( service_name ); + const size_t server_name_len = strlen( service_name ) + strlen( ".t" ) + 1; if ( server_name_len > MESSENGER_SERVICE_NAME_LENGTH ) { die( "Service name too long to create topology service name ( %s ).", service_name ); diff --git a/unittests/lib/libtopology_test.c b/unittests/lib/libtopology_test.c index e44b08d43..d51374670 100644 --- a/unittests/lib/libtopology_test.c +++ b/unittests/lib/libtopology_test.c @@ -1304,7 +1304,16 @@ main() { unit_test( test_respond_to_ping_from_topology ), }; + setenv( "TREMA_HOME", ".", 0); + // call before leak detector to suppress false leak detection + pid_t pid = get_trema_process_from_name( TOPOLOGY_BASE_NAME ); + if ( pid > 0 ) terminate_trema_process( pid ) ; + setup_leak_detector(); - return run_tests( tests ); + int test_result = run_tests( tests ); + + pid = get_trema_process_from_name( TOPOLOGY_BASE_NAME ); + if ( pid > 0 ) terminate_trema_process( pid ) ; + return test_result; }