Conceptual Idea

The topologygenerator is a tool for building a custom output file format out of a given network topology. The key concepts in this tool are the followings:

* Provider

Will be in charge of everything which is concerned to the network topology, meanning that it will have to provide information about it's elements, how they are connected between each other, general properties of each of the elements, etc., as well as has the ability to change it's content, for example adding new elements to the network, creating new connections, deleting elements, etc.

* NetworkTopology

Is an abastract representation of the network which will be build by several requests to the provider.

* Builder

Builders will know how to create from a network topology the desired output. For each type of element in the network (for example host, link, device), we will have a builder. This element is easily implemented thanks to the network topology explained above, since this abstraction allow us to decouple from the real provider (it wouldn't be the same to build an element if the provider is Onos or OpenDayLight, since the way of getting the information from the element varies depending on the controller).

* Output

Is what we are seeking, our main objective. This output can be either a file or multiple files, and will be generated by either one or multiple builders.

This tool was developed with the following two main focus in mind:

* Getting the network representation from SDN controller's (ONOS and OpenDayLight were the main objectives) to a graph's model for performing a semantical checking of Haikunet's program (this is covered in the next chapter)

* Getting the network from SDN controller's in order to create simulation models to the DEVS's tool. While performing this purpouse a network representation model was created, this allows the user to describe network topologies with some extra properties that will be useful to the simulation model. We will explain better this section in the Network Topology Model section.

What we have achieve in the developing process, besides the two objectives listed before, is to create a tool wich allows us to easily transform a network topology from a representation to another one, with just defining a few lines of code. This will be beter understood by the reader along this chapter.

We now proceed to understand how the elements relates one with each other with an abstract example of use. In this example, we will just have one provider and N type's of elements in the network as showed in the next image:

In this image we can see how the elements explanied above interact. The provider will communicate with the real network, in the image above the real network is represented as the network which is outside the topology generator, to get all the information necessary in order to create the network topology representation. This comunication can either happen locally or remotely as we will see in one of the followings examples.

Once the representation is created, the builders are called to generate the output. In this example, we consider having N different types of elements in the network topology, which make us to have N different types of builders (1 per each type). Just to remark what was explained about the output, we are showing an example where we have one output per builder. This is not necessary the only case, as we can see in this other example:

As we can see in this case, the logic of joining all the outputs from each of the builder's type is made by a new builder called TopologyBuilder, which is the one who finally generate's the desired output.

Now that we have a better understanding of the differents elements in the tool, we proceed to see some examples of use.

Examples of use

Let's consider now a concrete example where we want to obtain a network from either ONOS or OpenDayLight controller, and then create a DEVS's model for running simulations. We will assume that we are interested in hosts, devices and links of the network, meaning that we will consider only three types of elements.

In this example, we will need to have two providers, one per each controller, three builders, one per each type, and one more builder which will have the logic of joining all the output's provided to generate one common output, the topology.pdm, as show next:

Each of the providers in the image above, will have the knowledge to communicate with it's corresponding controller. In this example, the OnosProvider will encapsulate the logic to communicate with the Api Rest from the Onos controller, meanwhile the OpenDayLightProvider will do the same for the OpenDayLight controller.

The interaction with the controller can be either locally, for example this can happens if the controller is set up in the same machine where the topologygenerator is running, or remotely, for example if the controller is set up in a different machine and the comunication is made via http. How to implement both possibilities will be further explained in the next section. The output generated is a pdm which can be interpretated by the DEVS simulator.

What happen if we do not have a real network?, What if we have a representation of a network in a programming language, for example Ruby, and we want to create from this network a simulation to test properties?. Then we can use the topologygenerator as follow in the next image:

This image shows how it's just a question of creating a new provider, in this case the ruby provider, which will be in charge of having the knowledge of how to make the request to the script to obtain all the necessary information in order to create the network topology representation. Since we still want to create a simulation, the builders and the output is still the same.

In this previous example we show a powerful characteristic of the tool, which is that providers and builders are not related at all, meaning that changing a provider does not imply to change builders. This characteristic allow us then to create as much providers and builders as we want, and to connect them independently each other to generate as many desired output as we may want.

What would happend if we want to try out our custom topology written in Ruby, and test it in an Onos enviorment?, How could we manage to do this?.

In this example we would like to have our ruby provider, as in the previous case, and builders that generates request to the ONOS Api as show in the following image:

Each of the builder's in this case will generate requests to the ONOS Api that will create each of the elements in the SDN enviorment (this is a good example of the capability of SDN, since the enviorment allow us to create elements in the network).

In this example, it's important to notice how malleable and easy results to change ONOS, which was though as a provider in previous examples, to a desired output.

With the examples showed, we have a good understanding in the power of the tool and how it is supposed to be used. In the following section we will provide a tutorial for using the topologygenerator, and we will implement some of the examples showed before.

Tutorial

In this tutorial we will demonstrate how to install and use the topologygenerator tool. The tutorial was tested in a machine running Ubuntu 16.04, however should work in any machine which has a ruby version of 2.0 or major installed.

Installation

You will need to have Ruby installed in your machine. We recommend for this installing the ruby version manager (RVM), however any installation should be fine.

Once ruby is installed, you can proceed now to install the topologygenerator. For doing this, you will have two options:

* Installing it as a gem

You should go for this option in case you are thinking in using the topologygenerator as a library of your Ruby project.

* Installing it as a binary

Select this option in case you are not interested in adding the topologygenerator as a library of any project. In this case, you will use the topologygenerator as a binary.

Per each option please refer to the corresponding subsection listed below.

As a gem

Add this line to your application's Gemfile:

gem 'topologygenerator'

And then execute:

$ bundle

Or install it yourself as:

$ gem install topologygenerator

As a binary

Pre-requisite: Have installed and configured a git client.

Execute the followings lines (replace TOPOLOGY_DIRECTORY with the corresponding path):

$ git clone git@github.com:andyLaurito92/topologygenerator.git
$ sudo ln -s "$TOPOLOGY_DIRECTORY/lib/createTopology.rb" /usr/bin/topologygenerator

Using the tool

How to use the topologygenerator will depend in which of the installation's guide you have done, however both ways are not so different and remember that you can always have the tool installed as a gem and a binary at the same time. In case you have installed the tool as a binary, we encourage you to first read the Use as a gem section, and then proceed to the Use as a binary section, since it still will be useful.

Both tutorials require you to have installed either Onos or OpenDayLight Controller and mininet. First we will guide you in how to use the tool using the builders and providers implemented as examples, and then you will get hands on into the code and implement a provider and a builder. In both using examples, we will use the ONOS and OpenDayLight providers, and the PDM and Ruby builders.

NO ESTOY SEGURO SI SE PUEDEN USAR LOS BUILDERS Y PROVIDERS POR DEFECTO EN LA INSTALACIÓN DE LA GEMA, PORQUE NO SE SI RUBY TE DEJA ACCEDER A TODO EL PROYECTO DE LA GEMA, O SOLO AL BINARIO. TENGO QUE PROBAR ESTO, PERO EN CASO DE QUE NO SE PUEDA, ENTONCES EN LA INSTALACIÓN DE LA GEMA NO TENGO LOS BUILDERS Y PROVIDERS DE EJEMPLO, POR LO QUE TENGO QUE PENSAR EN COMO HACERLO ACCESIBLE PARA ESTOS MUCHACHOS.

As a gem

We will use the topologygenerator as a gem, meaning that will be part of your ruby code. Using the tool is really straightforward, first we require the gem in the script as follows:

require 'topologygenerator'

And now we can create a new topologygenerator instance as follows:

my_topology_generator = TopologyGenerator.new({
        "source" => "ONOS", #Actually ONOS, OPENDAYLIGHT,  OBJECT and CUSTOM are the options supported
        "directory_concrete_builders" => "builders_examples/pdm_builders", #The directory where builders are located
        "output_directory" => "output_directory", #The directory where the output will be saved
        "uri_resource" => "http://127.0.0.1/onos/v1/" #Must be the rest api uri if either ONOS or OpenDayLight is choosed or the path of a file if CUSTOM is choosed. In case OBJECT is choosed, you will have to provide a valid instance of Topology.
    })

Let's go through the code in more detailed. What we are doing in this example is to tell the topologygenerator that we are going to use the ONOS provider, this is done just by passing as argument "ONOS" to the source key, and the DEVS builders, for doing this you just need to detailed the path where the builders are located to the directory_concrete_builders key. Since we have the topologygenerator gem installed, the enviorment has already loaded the path to the project, so there is no need to use the full path.

The next thing that you have to do is set the path where the output is going to be stored, for doing you just need to define the output_directory key. Finally since we are using the ONOS provider, we need to provide the uri from the ONOS's api. As you can see, this is done just by defining the uri_resource key in the constructor.

After creating our topologygenerator instance, we can now proceed to generate the desired output by writing the following line in the code:

my_topology_generator.generate

The reader can have already noticed that what we are doing in this example, is implement the Controller's use case detailed in the previous section.

Before getting into the next example, let's go in more detailed in two of the four providers detailed above, the CUSTOM and OBJECT providers. For using the CUSTOM provider, you will need to provide the path to the ruby network topology model file, which it would contain a valid model. Assuming that your model is in the following path: models/my_network_model/my_network.rb, this is how you have to create the topologygenerator's instance:

my_topology_generator = TopologyGenerator.new({
        "source" => "CUSTOM",
        "directory_concrete_builders" => "builders_examples/pdm_builders",
        "output_directory" => "output_directory",
        "uri_resource" => "models/my_network_model/my_network.rb"
    })

For further information of what is this model and how you can program one, please go ahead to the Network topology model section.

For using the OBJECT provider, you will need to provide a valid instance of topology. Let's suppose that you have a variable called my_topology with a valid instance class, this is how you have to create your topologygenerator's instance:

my_topology_generator = TopologyGenerator.new({
        "source" => "OBJECT",
        "directory_concrete_builders" => "builders_examples/pdm_builders",
        "output_directory" => "output_directory",
        "uri_resource" => my_topology
    })

We encourage you to read more about the Topology class in the the topologygenerator API. In there you will be able to find examples in how to build a valid instance of this class.

Now that we have understood the providers, let's go trhough another example. Suppose that you want to use the tree topology network model located at network_topologies_examples, for creating a simulation in PowerDevs. This is the way of doing this:

my_topology_generator = Topologygenerator.new({
        "source" => "CUSTOM",
        "directory_concrete_builders" => "builders_examples/pdm_builders",
        "output_directory" => "output",
        "uri_resource" => "network_topologies_examples/tree_topology.rb" 
    })
my_custom_topology = topology_generator.generate

What about if you want to use an OpenDayLight controller running in your local host, and from this network get a ruby representation model?. Then you will have to do as follow:

my_topology_generator = Topologygenerator.new({
        "source" => "OPENDAYLIGHT",
        "directory_concrete_builders" => "builders_examples/ruby_builders",
        "output_directory" => "output",
        "uri_resource" => "http://localhost:8080/restconf/operational/network-topology:network-topology/topology/flow:1/" 
    }) 
my_custom_topology = topology_generator.generate

As you may have already noticed, using the topologygenerator tool is really straightforward and flexible. We encourage you to keep on trying the tool with another builders and providers!. When you think that you are ready, you can jump on to code your first provider in the Implementing your provider section!.

As a binary

These are the commands that you need to know for using the topologygenerator as a binary:

* -n (source name) : The name of the provider. This can be matched with the source key in the topologygenerator's constructor in the previous section, but you have three providers instead of four (OBJECT is not supported in this way of use).

* -o (output directory) : The path where the output will be stored. This can be matched with the output_directory key in the topologygenerator's constructor in the previous section.

* -u (uri resource) : A path to a ruby network model in case CUSTOM provider was selected, or an uri to an API if either ONOS or OPENDAYLIGHT were selected. This can be matched with the uri_resource key in the topologygenerator's constructor in the previous section.

* -d (directory concrete builders) : A path to a directory where the builders are located. This can be matched with the directory_concrete_builders in the topologygenerator's constructor in the previous section.

Remember that the path specified by argument can be relative, and in this case will be looked from the place where you are executing the command.

Suppose now that you want to use the topologygenerator for reading a network from ONOS, and build a simulation model. This is how you can do this:

$ topologygenerator source -n ONOS -o output_directory -u http://127.0.0.1/onos/v1/ -d builders_examples/pdm_builders

In case you want to try out the tree topology model for building a simulation, you can do it as follow:

$ topologygenerator source -n CUSTOM -o output_directory -u network_topologies_examples/tree_topology.rb -d builders_examples/pdm_builders

Or if you want to build a ruby model from an OpenDayLight network you can do the following:

$ topologygenerator source -n OPENDAYLIGHT -o output_directory -u http://localhost:8080/restconf/operational/network-topology:network-topology/topology/flow:1/ -d builders_examples/ruby_builders

Now that you know how to use the tool, let's proceed to get hands on and program your first provider!.

Implementing your provider

Provider's are one of the most important elements in the topologygenerator. Knowing how to implement a provider will give you the ability to extend the tool, and also to adapt the topologygenerator to your custom problem, are you ready to get started?.

In this tutorial we will show how to implement an OnosTopologyProvider, the only pre-requisite is to have ONOS installed either locally or remotely in order to test the implementation.

Every provider must inherit from a common interface called ITopologyProvider, this interface settles the methods that have to be implemented in the inherit classes. The methods mentioned are get_topology and get_path_between(source, destination)

CAMBIAR LA IMPLEMENTACIÓN DEL PROVIDER PARA QUE SEA COMO LA DEL BUILDER!. SE PUEDE UTILIZAR LA YA CLASE IMPLEMENTADA CUSTOM Y CON ESTA HACER LO MISMO QUE HICIMOS CON EL ABSTRACT BUILDER.

Implementing your builder

As explained before, builders are the ones that will generate your desired output, but for doing this we first need to know what we we want as output!. In this example, we will implement the ruby builder which we have already been talking about (It's recommended that you have read Network topology model section before jumping into this tutorial).

We have already seen when we use the tool, that every time we called the topologygenerator, doesn't matter if it's as a binary or a gem, we have to specify a directory where the builders are located. In this directory, the topologygenerator will always try first to load a file called output_concrete_builder.rb. This file has to contain a ruby module called OtputConcreteBuilder, with at least the following two methods:

* initialize_concrete_builder: This method will receive and instance of the topology provider being used in the ran, the path of the directory where the builders are (MAKE SENSES? WHY DID I DO THIS?) and the path of the output directory, that is where the desired output should be stored. * build_output_content: This method will be called in the process of generation, and it's expected to generate the output in the specified folder.

So let's start then by creating a folder called ruby_builders, and creating in there our first file called output_concrete_builder.rb . We already know some of the code that our file should have!, so let's start coding what we know up to know:

module OutputConcreteBuilder
   def initialize_concrete_builder(topology_provider, directory_concrete_builders, output_directory)
   end
    
    def build_output_content
    end
end

Great!, we have our base code. We know that our builder will generate a ruby model, and as how it was explained in the Network topology model section, this implies that we will be generating a module called NetworkTopology, which will have to methods: get_topology and get_path_between(source,destination). This model that we are going to generate is going to be written in a file that we will call ruby_network_topology.rb, and that we know that will be saved in the output directory. Using this knowledge let's start adding some generation code of what we know:

module OutputConcreteBuilder
   def initialize_concrete_builder(topology_provider, directory_concrete_builders, output_directory)
   end
    
    def build_output_content
       ruby_network_topology = "module NetworkTopology \n"
       ruby_network_topology += "    def get_topology \n "
       ruby_network_topology += "    end \n "
       ruby_network_topology += "    \n "
       ruby_network_topology += "    def get_path_between(source,destination) \n "
       ruby_network_topology += "    end \n "
       ruby_network_topology += "end \n "
    end
end

We have just defined a variable called ruby_network_topology, which will contain the content of our ruby model. Since know, we just defined the definition of the module, and the functions that we know that are going to be present.

An useful function for builders that the tool provides us is the write_file function. This function expect two arguments, first a string which would be the path where the file is going to be created in case it doesn't exist (it won't delete the file in case it already exsist), and second the content of the file to be created. In case the file already existed, the content will be overwritten.

Knowing that we can use this function, we can add the following lines to our output_concrete_builder.rb file:

module OutputConcreteBuilder
   def initialize_concrete_builder(topology_provider, directory_concrete_builders, output_directory)
      @output_directory = output_directory
   end
    
    def build_output_content
       ruby_network_topology = "module NetworkTopology \n"
       ruby_network_topology += "    def get_topology \n "
       ruby_network_topology += "    end \n "
       ruby_network_topology += "    \n "
       ruby_network_topology += "    def get_path_between(source,destination) \n "
       ruby_network_topology += "    end \n "
       ruby_network_topology += "end \n "
       
       write_file "#{@output_directory}/#{OUTPUT_RUBY_FILE_NAME}",
                    ruby_network_topology
    end
end

Two things were modified in the previous code, first the write_file function was added at the build_output_content method, and the definition of the instance variable @output_directory was done in the initialize_concrete_builder method. This instance variable was defined since we need to use it in the build_output_content function.

Right now we have a builder which generates as output an empty, but still valid, ruby model. We want basically to create from the provider, which was given to us in the initialize_concrete_builder, a network topology model. For doing this, we will need to get the elements of the network that the providers know, elements such as Host, Devices, Links, etc., and define this elements in our NetworkTopology model. Some things to consider before keep going are:

* We will just take into account the Host, Link and Router type in the Network. (Having different types as the mentioned above is not possible right know, however is not hard to extend the topologygenerator tool to have them. Please refer to the Limitiations section in this chapter for a better explanaition). *

Understanding our assumptions and considerations, let's keep going in defining an instance variable for the topology_provider instance that we have recieved in the initialize function:

module OutputConcreteBuilder
   def initialize_concrete_builder(topology_provider, directory_concrete_builders, output_directory)
      @topology_provider = topology_provider
      @output_directory = output_directory
   end
    
    def build_output_content
       ruby_network_topology = "module NetworkTopology \n"
       ruby_network_topology += "    def get_topology \n "
       ruby_network_topology += "    end \n "
       ruby_network_topology += "    \n "
       ruby_network_topology += "    def get_path_between(source,destination) \n "
       ruby_network_topology += "    end \n "
       ruby_network_topology += "end \n "
       
       write_file "#{@output_directory}/#{OUTPUT_RUBY_FILE_NAME}",
                    ruby_network_topology
    end
end

Now we can access the network topology elements from the build_output_content function using the get_topology method from the topology_provider variable (as we have seen in the Implementing your provider section, all providers have defined this method). While doing this, we will get all the elements of type Host, Link and Router as mentioned above.

module OutputConcreteBuilder
   def initialize_concrete_builder(topology_provider, directory_concrete_builders, output_directory)
      @topology_provider = topology_provider
      @output_directory = output_directory
   end
    
    def build_output_content
      network_elements = @topology_provider.get_topology
        
        network_elements = network_elements.select { |elem| [Host,Link,Router].include? elem.class }    
    
       ruby_network_topology = "module NetworkTopology \n"
       ruby_network_topology += "    def get_topology \n "
       ruby_network_topology += "    end \n "
       ruby_network_topology += "    \n "
       ruby_network_topology += "    def get_path_between(source,destination) \n "
       ruby_network_topology += "    end \n "
       ruby_network_topology += "end \n "
       
       write_file "#{@output_directory}/#{OUTPUT_RUBY_FILE_NAME}",
                    ruby_network_topology
    end
end

The class Host, Link and Router are directly provided by the topologygenerator when the module is included. These classes have implemented a method called transform_to_output_representation which recieves as argument a path to a directory, and can be called to ask for an instance of this classes to be serialize. The calling of this method will triggered a loader which would try to load in the directories path provided as argument, a file name after it's class with "concrete_builder.rb" appended at the end. For example, if we ask an instance of a host to be serialize (this means to call the method previous mentioned), the loader will try to load a file called Host_concrete_builder.rb in the directory provide as argument. If the method is called by a Link's instance, the loader will try to load a file called Link_concrete_builder.rb, and finally if the method is called by a Router's instance, the loader will try to load a file called Router_concrete_builder.rb. We show next what is the least expected content of these files: \begin{lstlisting}[language=Ruby,breaklines=true,title=Router_concrete_builder.rb] module RouterConcreteBuilder def build_output_representation end end

module HostConcreteBuilder    
    def build_output_representation
    end      
end

module LinkConcreteBuilder    
    def build_output_representation
    end      
end

In each of the build_output_representation functions is where you will have to program the code for serialize the instance. We will come back to this classes later.

Now that we have been provided of the network elements, it's time to define each of them as it is expected in the ruby model. Let's remember that in every ruby model we have the @topology instance variable, which is the place in where we are supposed to add the network elements. We first define three arrays, one per each type of element that we have in the topology. This arrays will be defined in the get_topology function of the ruby model as follow:

module OutputConcreteBuilder
   def initialize_concrete_builder(topology_provider, directory_concrete_builders, output_directory)
      @topology_provider = topology_provider
      @output_directory = output_directory
   end
    
    def build_output_content
      network_elements = @topology_provider.get_topology
        
        network_elements = network_elements.select { |elem| [Host,Link,Router].include? elem.class }    
    
       ruby_network_topology = "module NetworkTopology \n"
       ruby_network_topology += "    def get_topology \n "
       ruby_network_topology += "       hosts = [] \n "
       ruby_network_topology += "       routers = [] \n "
       ruby_network_topology += "       links = [] \n "
       ruby_network_topology += "    end \n "
       ruby_network_topology += "    \n "
       ruby_network_topology += "    def get_path_between(source,destination) \n "
       ruby_network_topology += "    end \n "
       ruby_network_topology += "end \n "
       
       write_file "#{@output_directory}/#{OUTPUT_RUBY_FILE_NAME}",
                    ruby_network_topology
    end
end

Now per each instance of elements that we are trying to add to the topology (let's remember that we have routers, hosts and links ), we will call the transform_to_output_representation method as follow:

module OutputConcreteBuilder
   def initialize_concrete_builder(topology_provider, directory_concrete_builders, output_directory)
      @topology_provider = topology_provider
      @output_directory = output_directory
      @directory_concrete_builders = directory_concrete_builders
   end
    
    def build_output_content
      network_elements = @topology_provider.get_topology
        
        network_elements = network_elements.select { |elem| [Host,Link,Router].include? elem.class }    
    
       ruby_network_topology = "module NetworkTopology \n"
       ruby_network_topology += "    def get_topology \n "
       ruby_network_topology += "       hosts = [] \n "
       ruby_network_topology += "       routers = [] \n "
   ruby_network_topology += "       links = [] \n "
   network_elements.each do |element|
      ruby_network_topology += "      #{element.transform_to_output_representation @directory_concrete_builders} \n"
        end
       ruby_network_topology += "    end \n "
       ruby_network_topology += "    \n "
       ruby_network_topology += "    def get_path_between(source,destination) \n "
       ruby_network_topology += "    end \n "
       ruby_network_topology += "end \n "
       
       write_file "#{@output_directory}/#{OUTPUT_RUBY_FILE_NAME}",
                    ruby_network_topology
    end
end

As you can see, we have defined the new instance variable @directory_concrete_builders for using it as argument to the transform_to_output_representation method. We now proceed to define each of the builder's class file per each type of element as it was explained above. We will explain in better detailed the Host_concrete_builder.rb builder, and for the other's one we will give directly the code.

module HostConcreteBuilder    
    def build_output_representation
       "hosts.push @topology.add_host \"#{id}\""
    end      
end

The code is straightforward, the function just add the host with it's corresponding id to the topology, and then pushes the result of this function to the hosts array previous defined. Notice that the function returns a string, since the idea is to serialize the host in the ruby model. It's important to remark that the context in which this function is called, is the same as if it were a method defined in the Host class, meaning that we can access to every property and method defined in the Host class (in this case, we are accessing the id method). This behaviour is repeated in both Router_concrete_builder.rb and Link_concrete_builder.rb.

module RouterConcreteBuilder    
    def build_output_representation
       "routers.push @topology.add_router \"#{id}\", #{priority_weights}"
    end      
end

module LinkConcreteBuilder    
    def build_output_representation
       "links.push @topology.add_link \"#{id}\", #{src_element.id}, #{src_port}, #{dst_element.id}, #{dst_port}, #{bandwith}"
    end      
end

Remember that these files should be in the same folder as the output_concrete_builder.rb file.

That's it!, you can now use your builder by specifying the path to the folder where the files are. You may have noticed that we didn't have implemented the get_path_between method. This is something that we left you to do it with some helps:

* Is your topology static?, if it is, you may want just to hardcode the path between each of the hosts/routers in the network * Is your topology dynamic?, if it is you perhaps need to implement a search path algorithm like Dijkstra.

Hope you have understood everything and you are interested in making your own builders!. If that is the case, please don't hesitate to contact me to add them in the repository.

Network Topology Model

The Network Topology Model (NTM from now on), it's a way to model networks in the ruby language. It was invented to fulfill the need of describing all the elements in a network, meaning that we wanted to model not just the physical elements (as for example Hosts, Routers, Links, etc.), but also the abstract elements (flows and paths for example). The way of describing a network in this model is really straightforward, and is totally dependent on the topologygenerator tool (as we will see in the following section).

You should use this model everytime that you want to describe, test some properties (as you will find out in the following chapter), or just discover a network. Now that you know what the NTM is and why it was program, go ahead to the implementing section and get your hands into the code!.

Implementing your network topology model

We will cover in this tutorial how we can manage to program our NTM for the following network

But first, let's understand the context in which we are going to use the model.

When you use the topologygenerator, between the provider's option there is one of the options that is CUSTOM. This options is thought for recieving as uri's argument a path to a file where the NTM model will be program, meaning that the model which will be program in this tutorial will be used as resource of the topologygenerator. The NTM is a ruby module with NetworkTopology as name, and two functions in it: get_topology and get_path_between(source, destination). The basic expected structure of this module is as follows:

module NetworkTopology
   def get_topology
   end
    
    def get_path_between(source, destination)
    end
end

Let's describe more into detailed both functions. The get_topology function will be the one, as it names remark, which will provide the network topology representation with all the elements described in the previous section (Host, Routers, Links, Paths, and Flows). This elements will have to be added to the instance variable @topology which is already available in the module (to understand better why this happens, we encouraged the reader to study the CustomTopologyProvider class from the Appendix).

The get_path_between(source,destination) function will receive two instances from either Router or Host classes, and is expected to return an instance of Path with the corresponding path between both nodes (in case more than one exist, is the user responsability to select one). Since our network is harcoded, meaning that we already know the topological structure of it, this function will be trivial.

Let's describe then our desired topology. We have three hosts and one router, and every host will be connected to the router, and no host will be connected between each other. Let's first start by adding the routers and host to the topology.

module NetworkTopology
   def get_topology
      routers = []
        hosts = []

        router = @topology.add_router "MyRouter1"
        routers.push router

        for i in 0..2  
          host = @topology.add_host "Host#{i}"
          hosts.push host     
        end
   end
    
    def get_path_between(source, destination)
    end
end

Easy right?, we just need to add our elements with the desired properties to the topology and that's it!. We encouraged the reader to stop now this tutorial, and go and check the Topology api to see what properties can be set to each of the elements in the network. As you will se from the api, we are just setting the basics properties.

As you may have noticed, we have created two arrays, one per each element. This arrays will help us now in the creation of links, since we now have to create a link which has to receieve at least as argument it's source node, the port in which the link is connected in the source, it's destination node, and the port in which the link is connected in the destination node. By default adding a links is half duplex, meaning that if you want to have double communication you have either to add two links or use the add_full_duplex_link method from @topology. In this example we will assume that we want the double connection, and we will use the first way of adding two links just to show how you should do this. The code will look as follow:

module NetworkTopology
   def get_topology
      routers = []
        hosts = []

        router = @topology.add_router "MyRouter1"
        routers.push router

        for i in 0..2  
          host = @topology.add_host "Host#{i}"
          hosts.push host     
        end
        
        #Links (starting from the flows that goes from top to bottom)    
        link1 = @topology.add_link "Link1", hosts[0], 0, routers[0], 0
        link2 = @topology.add_link "Link2", hosts[1], 0, routers[0], 1

        link3 = @topology.add_link "Link3", routers[0], 0, hosts[2], 0

        #Links (flows that goes from bottom to top)    
        link4 = @topology.add_link "Link4", hosts[2], 0, routers[0], 2

        link5 = @topology.add_link "Link5", routers[0], 1, hosts[0], 0
        link6 = @topology.add_link "Link6", routers[0], 2, hosts[1], 0
        
   end
    
    def get_path_between(source, destination)
    end
end

Here you can see how the host and router's arrays defined before take an important role for the link's definition. Looks a bit tricky at first, but if you stop a minute and see what we are doing here, is just to identify each host name per it's index, and then connect it to the router as we were supposed to by the model's image above.

This function must always return the elements of the topology, this is how you do it:

module NetworkTopology
   def get_topology
      routers = []
        hosts = []

        router = @topology.add_router "MyRouter1"
        routers.push router

        for i in 0..2  
          host = @topology.add_host "Host#{i}"
          hosts.push host     
        end
        
        #Links (starting from the flows that goes from top to bottom)    
        link1 = @topology.add_link "Link1", hosts[0], 0, routers[0], 0
        link2 = @topology.add_link "Link2", hosts[1], 0, routers[0], 1

        link3 = @topology.add_link "Link3", routers[0], 0, hosts[2], 0

        #Links (flows that goes from bottom to top)    
        link4 = @topology.add_link "Link4", hosts[2], 0, routers[0], 2

        link5 = @topology.add_link "Link5", routers[0], 1, hosts[0], 0
        link6 = @topology.add_link "Link6", routers[0], 2, hosts[1], 0
        
        @topology.topology_elements
   end
    
    def get_path_between(source, destination)
    end
end

And that's it for the get_topology function. We can now finish this tutorial by implementing our get_path_between(source, destination) function. Like we said before, this would be really straightforward, since everyone is connected to everyone in either one or two steps!. This is an example of implementation of the get_path function (and it's not the only possible!).

module NetworkTopology
   def get_topology
      routers = []
        hosts = []

        router = @topology.add_router "MyRouter1"
        routers.push router

        for i in 0..2  
          host = @topology.add_host "Host#{i}"
          hosts.push host     
        end
        
        #Links (starting from the flows that goes from top to bottom)    
        link1 = @topology.add_link "Link1", hosts[0], 0, routers[0], 0
        link2 = @topology.add_link "Link2", hosts[1], 0, routers[0], 1

        link3 = @topology.add_link "Link3", routers[0], 0, hosts[2], 0

        #Links (flows that goes from bottom to top)    
        link4 = @topology.add_link "Link4", hosts[2], 0, routers[0], 2

        link5 = @topology.add_link "Link5", routers[0], 1, hosts[0], 0
        link6 = @topology.add_link "Link6", routers[0], 2, hosts[1], 0
        
        @topology.topology_elements
   end
    
    def get_path_between(source, destination)
       first_link = @topology.topology_elements.select { |elem| (elem.is_a? Link) && (elem.src_element == source) }.first
        second_link = @topology.topology_elements.select { |elem| (elem.is_a? Link) && (elem.dst_element == destination) }.first

        path = Path.new(source,destination)        
        path.add_link first_link
        path.add_link second_link unless first_link.dst_element == destination
        path
    end
end

In this implementation we are just looking for the links that match the source and destiny nodes, add the first link always, and check if the second link should be added (remember that we can be asked by a host and router's path, which in this case means that only one link should be added).

Congratulations!, you have implemented your first ruby NTM!, you can now go ahead and check it out, and start playing around with the example programed. In the next subsection we will cover how to add Paths with Flows in the network implemented.

Adding Paths with Flows

We will cover now how to add paths and flows in the following context: Suppose that we are thinking on setting up the enviorment previous model, this means buying all the equipment for the hosts, thinking in which would be the best links for the network, which type of router should we buy?.

This type of questions are usually made in the setting up of any network enviorment, and are usually answered with statistics of the network traffic, assumptions of traffic generation from each of the elements in the network, etc. This network trafic assumptions, or even the network traffic statistics most of the times can be aproximated by mathematical's distribution, and uniting this knowldge with DEVS, we can easily simulate a NTM by just describing it's characteristics in this model, and then generating the DEVS's model for running a simulation. Using then the previous model, and the assumption that each of the hosts will generate traffic following an exponential distribution, and the size of the packets will follow a constant distribution, we can add three flows per host as follows:

module NetworkTopology
   def get_topology
      routers = []
        hosts = []

        router = @topology.add_router "MyRouter1"
        routers.push router

        for i in 0..2  
          host = @topology.add_host "Host#{i}"
          hosts.push host     
        end
        
        #Links (starting from the flows that goes from top to bottom)    
        link1 = @topology.add_link "Link1", hosts[0], 0, routers[0], 0
        link2 = @topology.add_link "Link2", hosts[1], 0, routers[0], 1

        link3 = @topology.add_link "Link3", routers[0], 0, hosts[2], 0

        #Links (flows that goes from bottom to top)    
        link4 = @topology.add_link "Link4", hosts[2], 0, routers[0], 2

        link5 = @topology.add_link "Link5", routers[0], 1, hosts[0], 0
        link6 = @topology.add_link "Link6", routers[0], 2, hosts[1], 0
        
      flow_1_path = Path.new hosts[0], hosts[2]
        flow_1_path.add_link link1
        flow_1_path.add_link link3
        @topology.add_flow "Flow1", 
                            10, 
                            [flow_1_path], 
                            (ExponentialDistribution.new 1.0/6875), 
                            (ConstantDistribution.new 1000*8)

        flow_2_path = Path.new hosts[1], hosts[2]
        flow_2_path.add_link link2
        flow_2_path.add_link link3
        @topology.add_flow "Flow2", 
                            15, 
                            [flow_2_path], 
                            (ExponentialDistribution.new 1.0/6875), 
                            (ConstantDistribution.new 1000*8)

        flow_3_path = Path.new hosts[2], hosts[1]
        flow_3_path.add_link link4
        flow_3_path.add_link link5
        @topology.add_flow "Flow3", 
                            20, 
                            [flow_3_path], 
                            (ExponentialDistribution.new 1.0/6875), 
                            (ConstantDistribution.new 1000*8)         
        
        @topology.topology_elements
   end
    
    def get_path_between(source, destination)
       first_link = @topology.topology_elements.select { |elem| (elem.is_a? Link) && (elem.src_element == source) }.first
        second_link = @topology.topology_elements.select { |elem| (elem.is_a? Link) && (elem.dst_element == destination) }.first

        path = Path.new(source,destination)        
        path.add_link first_link
        path.add_link second_link unless first_link.dst_element == destination
        path
    end
end

Let's explain in more detailed what we are doing in the Flow1 definition (this explanaition will cover all the three flow's definitions). What we first do is define a path between two hosts in this line:

flow_1_path = Path.new hosts[0], hosts[2] 

The first one correspond to the flow's source, and the next one corresponds to the flow's destination. We then add a valid path between both nodes, and we finally use the add_flow method from the Topology class to define a new flow between these nodes. If you want a better explanaition of each of the arguments received by the function, we encouraged you to read the Topology API.

Future work

There are a lot of things that still need to be done in this tool:

* Extend the SDN Controllers provider's for retrieving more information

The information fetched now in the tool is enough, but we are not taking advantage of all the information that we had!. Could we define flows using the entities flow that exist in each of the controllers?, Do they represent the same concept?, Could we define the flow distribution's just by using controller's statistics?

* Adapt the providers to have the same behaivour as builders

Providers and builders are very similar in code as you have already seen. Both should be implemented in the same way, and having the builder's implementation for providers will allow a better extensibility of the tool, since now we are limited to the providers that exist, and adding or modifying a provider implies changing the tool code.

* Add more providers and builders

The number of providers and builders that are now implemented is really small. Providers that would be interest to have would be: a provider for the Topology Zoo topologies topologyzoo, another for InfiniBand infiniband, Can this last provider be done?, What changes would it imply?. In the other hand, it would be interest to have builders for the controller's, for OMNeT++ omnet in order to extend the reacheability of the tool

* Allow the posibility to extend network entities

If we can manage to allow the user to extend the network entities, or even use different entities, we would make the tool much more abstract and powerful. What would be needed for doing this?, Can we do something similar as we did with the builders?

Edit | Attach | Watch | Print version | History: r1 | Backlinks | Raw View | WYSIWYG | More topic actions
Topic revision: r1 - 2017-01-23 - unknown
 
    • Cern Search Icon Cern Search
    • TWiki Search Icon TWiki Search
    • Google Search Icon Google Search

    Main All webs login

This site is powered by the TWiki collaboration platform Powered by PerlCopyright &© 2008-2024 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
or Ideas, requests, problems regarding TWiki? use Discourse or Send feedback