From 42ed73812ded545f2f7a59182ddf7159889e2528 Mon Sep 17 00:00:00 2001 From: HIGUCHI Yuta Date: Thu, 21 Feb 2013 12:32:50 +0900 Subject: [PATCH] Auto start topology daemon. (#5) --- Rantfile | 7 ++ .../topology.show_switch_status.feature | 30 ++--- .../examples/topology.show_topology.feature | 114 ++++++++++++++---- src/examples/topology/.gitignore | 6 + src/examples/topology/README.md | 29 +++-- src/examples/topology/enable_discovery.c | 77 ++++++++++++ src/examples/topology/topology.conf | 12 -- src/examples/topology/topology_fullmesh.conf | 12 -- src/lib/topology.c | 103 +++++++++++++++- unittests/lib/libtopology_test.c | 57 +++++---- 10 files changed, 352 insertions(+), 95 deletions(-) create mode 100644 src/examples/topology/.gitignore create mode 100644 src/examples/topology/enable_discovery.c 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..876c521aa 100644 --- a/src/lib/topology.c +++ b/src/lib/topology.c @@ -25,9 +25,15 @@ #include #include #include +#include +#include +#include +#include #include "trema.h" #include "topology.h" +bool disable_auto_start_topology_daemon = false; + static char *libtopology_queue_name = NULL; static char *topology_name = NULL; @@ -299,6 +305,7 @@ recv_port_status_notification( uint16_t tag, void *data, size_t len ) { ( *port_status_updated_callback )( port_status_updated_callback_param, s ); } + static void recv_switch_status_notification( uint16_t tag, void *data, size_t len ) { UNUSED( tag ); @@ -318,13 +325,12 @@ recv_switch_status_notification( uint16_t tag, void *data, size_t len ) { (* switch_status_updated_callback )( switch_status_updated_callback_param, switch_status ); } + static void recv_ping_request( const messenger_context_handle *handle, void *data, size_t len ) { UNUSED( len ); UNUSED( data ); -// topology_request *req = data; - // respond to topology ping const size_t name_bytes = strlen( libtopology_queue_name ) + 1; const size_t req_len = sizeof(topology_ping_response) + name_bytes; @@ -342,6 +348,7 @@ recv_ping_request( const messenger_context_handle *handle, void *data, size_t le free_buffer( buf ); } + static void recv_topology_response( uint16_t tag, void *data, size_t len, void *param0 ) { UNUSED( tag ); @@ -402,6 +409,7 @@ recv_unsubscribe_reply( uint16_t tag, void *data, size_t len, void *param0 ) { recv_topology_response(tag, data, len, param0); } + bool subscribe_topology( void ( *callback )( void *user_data, topology_response *res ), void *user_data ) { if( is_subscribed ){ @@ -410,6 +418,7 @@ subscribe_topology( void ( *callback )( void *user_data, topology_response *res return send_request( TD_MSGTYPE_SUBSCRIBE_REQUEST, callback, user_data ); } + bool unsubscribe_topology( void ( *callback )( void *user_data, topology_response *res ), void *user_data ) { if( !is_subscribed ){ @@ -418,11 +427,13 @@ unsubscribe_topology( void ( *callback )( void *user_data, topology_response *re return send_request( TD_MSGTYPE_UNSUBSCRIBE_REQUEST, callback, user_data ); } + bool enable_topology_discovery( void ( *callback )( void *user_data, const topology_response *res ), void *user_data ) { return send_request( TD_MSGTYPE_ENABLE_DISCOVERY_REQUEST, callback, user_data ); } + bool disable_topology_discovery( void ( *callback )( void *user_data, const topology_response *res ), void *user_data ) { return send_request( TD_MSGTYPE_DISABLE_DISCOVERY_REQUEST, callback, user_data ); @@ -502,6 +513,7 @@ recv_status_notification( uint16_t tag, void *data, size_t len ) { } } + static void recv_request( const messenger_context_handle *handle, uint16_t tag, void *data, size_t len ) { @@ -517,6 +529,7 @@ recv_request( const messenger_context_handle *handle, } } + static void check_transaction_table( void *user_data ) { hash_table *transaction_table = user_data; @@ -536,6 +549,89 @@ 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 ) { + if ( disable_auto_start_topology_daemon ) return; + + // 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 +639,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 ); @@ -568,6 +666,7 @@ init_libtopology( const char *service_name ) { return true; } + bool add_callback_switch_status_updated( void ( *callback )( void *user_data, const topology_switch_status *link_status ), diff --git a/unittests/lib/libtopology_test.c b/unittests/lib/libtopology_test.c index e44b08d43..3fafd482f 100644 --- a/unittests/lib/libtopology_test.c +++ b/unittests/lib/libtopology_test.c @@ -19,7 +19,7 @@ #include "event_handler.h" #include "messenger.h" - +extern bool disable_auto_start_topology_daemon; /******************************************************************************** * Common function. @@ -313,6 +313,13 @@ finalize_libtopology_mock() { static void setup() { + disable_auto_start_topology_daemon = true; +} + + +static void +setup_mocks() { + setup(); swap_original( add_message_requested_callback ); swap_original( delete_message_requested_callback ); swap_original( add_message_replied_callback ); @@ -329,13 +336,19 @@ setup() { static void setup_libtopology() { - setup(); + setup_mocks(); init_libtopology_mock(); } static void teardown() { + disable_auto_start_topology_daemon = false; +} + + +static void +teardown_mocks() { revert_original( add_message_requested_callback ); revert_original( delete_message_requested_callback ); revert_original( add_message_replied_callback ); @@ -347,13 +360,15 @@ teardown() { revert_original( delete_timer_event ); revert_original( send_request_message ); + + teardown(); } static void teardown_libtopology() { finalize_libtopology_mock(); - teardown(); + teardown_mocks(); } /******************************************************************************** @@ -1275,34 +1290,34 @@ main() { const UnitTest tests[] = { unit_test( test_struct_size ), - unit_test_setup_teardown( test_init_libtopology, setup, teardown ), - unit_test_setup_teardown( test_second_init_libtopology_should_fail, setup, teardown ), - unit_test_setup_teardown( test_finalize_libtopology, setup, teardown ), - unit_test_setup_teardown( test_second_finalize_libtopology_should_fail, setup, teardown ), + unit_test_setup_teardown( test_init_libtopology, setup_mocks, teardown_mocks ), + unit_test_setup_teardown( test_second_init_libtopology_should_fail, setup_mocks, teardown_mocks ), + unit_test_setup_teardown( test_finalize_libtopology, setup_mocks, teardown_mocks ), + unit_test_setup_teardown( test_second_finalize_libtopology_should_fail, setup_mocks, teardown_mocks ), - unit_test_setup_teardown( test_subscribe_topology, setup, teardown ), - unit_test( test_duplicate_subscribe_topology ), + unit_test_setup_teardown( test_subscribe_topology, setup_mocks, teardown_mocks ), + unit_test_setup_teardown( test_duplicate_subscribe_topology, setup, teardown ), unit_test_setup_teardown( test_subscribe_topology_send_fail, setup_libtopology, teardown_libtopology ), - unit_test_setup_teardown( test_unsubscribe_topology, setup, teardown ), - unit_test( test_duplicate_unsubscribe_topology ), + unit_test_setup_teardown( test_unsubscribe_topology, setup_mocks, teardown_mocks ), + unit_test_setup_teardown( test_duplicate_unsubscribe_topology, setup, teardown ), unit_test_setup_teardown( test_unsubscribe_topology_send_fail, setup_libtopology, teardown_libtopology ), - unit_test( test_get_all_link_status ), - unit_test( test_get_all_port_status ), - unit_test( test_get_all_switch_status ), + unit_test_setup_teardown( test_get_all_link_status, setup, teardown ), + unit_test_setup_teardown( test_get_all_port_status, setup, teardown ), + unit_test_setup_teardown( test_get_all_switch_status, setup, teardown ), - unit_test( test_enable_topology_discovery ), - unit_test( test_disable_topology_discovery ), + unit_test_setup_teardown( test_enable_topology_discovery, setup, teardown ), + unit_test_setup_teardown( test_disable_topology_discovery, setup, teardown ), - unit_test( test_set_link_status ), + unit_test_setup_teardown( test_set_link_status, setup, teardown ), unit_test_setup_teardown( test_set_link_status_send_fail, setup_libtopology, teardown_libtopology ), - unit_test( test_add_callback_switch_status_updated ), - unit_test( test_add_callback_link_status_updated ), - unit_test( test_add_callback_port_status_updated ), + unit_test_setup_teardown( test_add_callback_switch_status_updated, setup, teardown ), + unit_test_setup_teardown( test_add_callback_link_status_updated, setup, teardown ), + unit_test_setup_teardown( test_add_callback_port_status_updated, setup, teardown ), - unit_test( test_respond_to_ping_from_topology ), + unit_test_setup_teardown( test_respond_to_ping_from_topology, setup, teardown ), }; setup_leak_detector(); return run_tests( tests );