• Nenhum resultado encontrado

APIs: implementações intermediárias

3.3 COMPOSIÇÃO DAS ESTRATÉGIAS

3.3.1 Mapeamento e Desenvolvimento

3.3.1.1 APIs: implementações intermediárias

Com o objetivo de oferecer suporte e intermediar as requisições das estratégias apresentadas anteriormente foi necessário desenvolver duas APIs, localizadas na camada de acesso como apresentado na figura 8, que atendessem tanto às aplicações baseadas em Web

Services (WS) REST e SOAP quanto CLI e NETCONF. Essas APIs trabalham, em nível de

camada de acesso, como se fossem gateways entre as aplicações e o dispositivo de rede a ser gerenciado. Intrínseco a elas estão a responsabilidade de conter o mapeamento dos comandos CLI e NETCONF relativos às tarefas de gerência de configuração elencadas no quadro 1 da seção 3.2, automatizando assim a sua execução.

Para realizar a conexão entre as APIs e o equipamento de rede foi utilizada a biblioteca Ganymed SSH-29 disponível sob uma licença New BSD License, que traz uma implementação do protocolo SSH-2 baseada na linguagem Java.

6

http://download.oracle.com/otn/java/jdk/8u51-b16/jdk-8u51-windows-x64.exe 7 http://download.oracle.com/otn/java/jdk/8u51-b16/jre-8u51-windows-x64.exe

Como exemplo, no trecho de código do quadro 6 tem-se a simulação por meio de

software para a tarefa de número 12, a qual objetiva a configuração de uma interface (porta)

de rede através dos parâmetros port, interfaceAddress, subnetMask, MTU e description por meio de um método disposto na API CLI. Esses parâmetros em conjunto a um objeto do tipo

Router fornecem, após a execução do método, uma nova configuração para a interface de rede

desejada.

Quadro 6 – Mapeamento da configuração de interface de rede através da API CLI.

/**

* Método responsável pela execução da configuração de uma interface * de rede através da CLI

* @param router - Objeto que contém o host (endereço IP), usuário e senha * @param port

* @param address * @param subnetMask * @param MTU

* @param description

* @return String - Contendo o spool do dispositivo após a execução dos comandos */

public static String configureInterface(Router router, String port, String address, StringsubnetMask,String MTU, String description) {

//Crio um array para empilhar os comandos List<String> comandos = new ArrayList<>();

comandos.add("conf t");

comandos.add("interface GigabitEthernet " + port);

comandos.add("ip address " + address + " " + subnetMask);

comandos.add("mtu " + MTU);

comandos.add("description " + description);

comandos.add("no shutdown");

comandos.add("end");

//Crio um StringBuilder de retorno StringBuilder sb = new StringBuilder();

try {

//Chamo a função responsável por executar os comandos

sb = ExecMultipleCommands.RunMultipleCommands(router, comandos);

} catch (ExceptionSSHConnection ex) {

System.out.println(ex.getMessage());

} catch (Exception exa) {

System.out.println(exa.getMessage());

}

//Faço o replace para devolver a resposta já formatada com as quebras de linhas //idênticas ao acesso via CLI realizado manualmente

return sb.toString().replaceAll("@", "\n"); }

Fonte: Elaborada pelo autor.

Para execução do método apresentado pelo quadro 6, primeiramente deve-se realizar a conexão com o dispositivo de rede. O trecho de código apresentado no quadro 7 demonstra a realização dessa conexão através do protocolo SSH-2. Esse método recebe como parâmetro um objeto do tipo device que contém os atributos host, user e password. Nota-se nesse trecho a orientação a objetos na extensão das classes que tratam dos objetos routers e devices

(Router extends Device), pois todo roteador é um dispositivo de rede, com características

específicas, mas nem todo dispositivo de rede é um roteador. Nesse caso a realização da

conexão SSH-2 é genérica, servindo para qualquer dispositivo de rede – tomando como padrão a porta 22 para o estabelecimento da conexão.

Quadro 7 – Conexão com o dispositivo de rede através da API CLI.

/**

* Método responsável por realizar a conexão com o dispositivo de rede através * do protocolo SSH

* @param device - Objeto que contém o host (endereço IP), usuário e senha * @return Connection - Um objeto contendo a conexão aberta

* @throws IOException */

public static Connection getConnection(Device device) throws IOException {

//Crio um novo objeto conexão e informo o host Connection conn = new Connection(device.getHost());

conn.connect();

//Conecto no dispositivo de rede informando usuário e senha

boolean isAuthenticated = conn.authenticateWithPassword(device.getUser(),

device.getPassword());

//Verifico se a autenticação foi realizada if (isAuthenticated == false) {

throw new IOException("Autenticação falhou.");

}

//Retorno um objeto contendo a conexão realizada. return conn;

}

Fonte: Elaborada pelo autor.

Entretanto, para executar uma tarefa de gerência de configuração no dispositivo, além de realizar a conexão é necessário abrir uma sessão SSH-2 emulando um terminal virtual. Essa operação é apresentada no quadro 8 e realizada no mesmo método responsável por enviar e executar os comandos no roteador. Após o envio e a execução desses comandos o parâmetro de retorno é encapsulado em um objeto contendo todo o spool oriundo do dispositivo de rede. Durante a comunicação entre a API CLI e o dispositivo de rede existem basicamente duas trocas de mensagens: o envio dos comandos e o recebimento das respostas, diferenciando-se da API NETCONF onde a troca de mensagens em uma mesma sessão é intermitente, cessando e recomeçando por intervalos definidos pelos agentes e tipos de mensagens trocadas.

Quadro 8 – Método responsável pela execução dos comandos através da API CLI.

/**

* Método responsável por executar os comandos passados * como parâmetro para o roteador

* @param connection * @param outputListener * @param commands

* @return StringBuilder - Contendo o retorno dos comandos executados no roteador * @throws IOException

* @throws UnsupportedEncodingException */

private static StringBuilder execCommands(Connection connection,

OutputStream outputListener,

List<String> commands) throws IOException,UnsupportedEncodingException {

//Crio um StringBuilder de saída

StringBuilder output = new StringBuilder();

//Utilizo a conexão criada para abrir uma sessão Session session = connection.openSession();

try {

//Emulo um terminal virtual VT100

//Inicio a shell desse terminal session.startShell();

//Crio um stream para escrever os comandos na entrada na sessão

OutputStreamWriter writer = null;

writer = new OutputStreamWriter(session.getStdin(), "utf-8");

//Itero nos comandos passados como parâmetros e escrevo

//na entrada da sessão for (String temp : commands) {

writer.write(temp);

writer.flush();

}

//Leio o spool oriundo do roteador após a execução dos comandos

output = ConnectionReturns.getOutput(session, outputListener);

//Finalizo o stream writer.close(); //Finalizo a sessão } finally { session.close(); }

//Dou o retorno do método com o spool return output;

}

Fonte: Elaborada pelo autor.

O diagrama contendo algumas classes utilizadas pela API CLI é apresentado na figura 9.

Figura 9 – Diagrama de classes da API CLI.

Fonte: Elaborado pelo autor.

Durante o desenvolvimento da API NETCONF foi necessário alterar os procedimentos de abertura de conexão e sessão, motivado pelo fato de que quando uma sessão é aberta entre os pares (cliente/aplicação – servidor/dispositivo de rede) é realizada uma troca de mensagens como apresentado na seção 2.1.2.3. Caso não seja respeitada a sequência e

estrutura dessa comunicação, a conexão não é estabelecida. Com a necessidade de manter uma sessão aberta durante essa troca de mensagens, foi utilizado um design pattern (padrão de projeto) chamado singleton, implementando assim uma fábrica de conexões e sessões de forma a limitar a existência de uma única instância para cada objeto.

O trecho de código apresentado no quadro 9 define a classe responsável pela criação da fábrica de conexões. Com a utilização dessa fábrica, e caso uma conexão já tenha sido realizada para a execução de algum comando, somente o objeto preexistente é retornado; do contrário, é iniciado o procedimento de conexão similar à API CLI.

Quadro 9 – Fábrica de conexões da API NETCONF.

/***

* Classe responsável pela fábrica de conexões implementando o padrão * de projeto singleton

* @author giv-NB */

public class ConnectionFactory {

//Declaro o objeto do tipo Connection como público e passível de ser reaproveitado public static Connection con;

/***

* Método responsável por realizar a conexão com o dispositivo * de rede através do protocolo SSH-2

* mantendo somente uma instância para o objeto * @param device

* @return Connection - Um objeto contendo a conexão aberta * @throws IOException

*/

public static Connection getConnection(Device device) throws IOException {

//Se o objeto estiver nulo

if (con == null) {

//Crio um novo objeto conexão e informo o host

con = new Connection(device.getHost());

//Conecto no dispositivo de rede informando usuário e senha

con.connect();

boolean isAuthenticated = con.authenticateWithPassword(device.getUser(),

device.getPassword());

//Verifico se a autenticação foi realizada e retorno um

//objeto contendo a conexão.

if (isAuthenticated == false) {

throw new IOException("Autenticação falhou.");

}

return con;

//Se o objeto não estiver nulo então a conexão para a execução

//corrente já foi realizada logo retorno o objeto criado

} else {

return con;

}

} }

Fonte: Elaborada pelo autor.

A mesma analogia da fábrica de conexões foi utilizada na criação das sessões, diferenciando-se da API CLI pelo fato de que a sessão é delegada a uma classe separada e não dentro de um método responsável pela execução dos comandos. Outra característica que a diferencia da API CLI foi a utilização do subsistema10 NETCONF para estabelecer a sessão com o dispositivo de rede, firmando assim o uso desse protocolo durante a troca de

mensagens entre os pares. A metodologia aplicada na fábrica de sessões é apresentada no quadro 10.

Quadro 10 – Fábrica de sessões da API NETCONF.

/**

* Classe responsável pela fábrica de sessões NETCONF * implementando o padrão de projeto singleton

* @author giv-NB */

public class SessionFactory {

//Declaro o objeto do tipo Session como público e passível de ser reaproveitado public static Session session;

/**

* Método responsável por iniciar a sessão com o dispositivo de rede * através do protocolo SSH-2

* @param connection

* @return Session - Um objeto contendo uma sessão aberta * utilizando o protocolo NETCONF

* @throws IOException */

public static Session getSession(Connection connection) throws IOException {

//Se o objeto estiver nulo if (session == null) {

//Abro a sessão

session = connection.openSession();

//E ao invês de utilizar VT100 (da API CLI) inicio o subsistema para

//trabalhar com o protocolo NETCONF

session.startSubSystem("netconf");

//Retorno a sessão ao método

return session;

//O objeto já está criado então somente o retorno

} else {

return session;

}

} }

Fonte: Elaborada pelo autor.

O envio e a execução de comandos através da API NETCONF são apresentados pelo método execCommands do quadro 11. Diferente da API CLI, esse método utiliza a fábrica de sessões para estabelecer uma comunicação com o dispositivo de rede e envia uma mensagem (comando) por vez, respeitando a sequência de troca de mensagens imposta pelo protocolo NETCONF. Cabe à aplicação NETCONF, hospedada na camada de apresentação, organizar e sequenciar essa troca de mensagens.

Quadro 11 – Método responsável pela execução de comandos através da API NETCONF.

/***

* Método responsável por executar os comandos NETCONF, * passados como parâmetro, no roteador

* @param connection * @param outputListener * @param commands

* @return StringBuilder - Contendo o spool do dispositivo após a execução dos comandos * @throws IOException

* @throws UnsupportedEncodingException */

private static StringBuilder execCommands(Connection connection,

OutputStream outputListener,

String commands)

throws IOException,UnsupportedEncodingException {

//Crio um StringBuilder de saída

StringBuilder output = new StringBuilder();

//Utilizo a fábrica de sessões para início da sessão SSH-2 pelo subsistema NETCONF Session session = SessionFactory.getSession(connection);

try {

//Crio um stream para escrever os comandos na entrada da sessão

OutputStreamWriter writer = null;

writer = new OutputStreamWriter(session.getStdin(), StandardCharsets.UTF_8);

//Escrevo o comando na entrada da sessão -

//Como só vem um XML por vez não preciso iterar

writer.write(commands + "\n");

writer.flush();

//Leio o spool oriundo do roteador após a execução do comando

output = ConnectionReturns.getOutput(session, outputListener);

//Fecho o stream mas não finalizo a sessão

writer.close();

}

//Dou o retorno do método com o spool return output;

}

Fonte: Elaborada pelo autor.

Outro ponto que difere a API NETCONF da API CLI é de que os comandos imutáveis estão dispostos em constantes e não em métodos – ver quadro 12. Para os comandos variáveis, que necessitam da substituição das informações estruturais, a aplicação NETCONF realiza a modificação para que uma demanda específica possa ser atendida – por exemplo, a criação de uma rota ou a configuração individual de uma interface de rede.

Quadro 12 – Declaração das constantes utilizadas pela API NETCONF.

/**

* Classe responsável por abrigar as constantes (comandos NETCONF) * utilizados para gerência de configuração

* @author giv-NB */

public class NETCONFConstants {

//Hello enviado para estabelecimento da comunicação public static String hello

= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" + "<capabilities>\n" + "<capability>urn:ietf:params:netconf:base:1.0</capability>\n" + "<capability>urn:ietf:params:netconf:capability:writeable-running:1.0</capability>\n" + "<capability>urn:ietf:params:netconf:capability:startup:1.0</capability>\n" + "<capability>urn:ietf:params:netconf:capability:url:1.0</capability>\n" + "<capability>urn:cisco:params:netconf:capability:pi-data-model:1.0</capability>\n" + "<capability>urn:cisco:params:netconf:capability:notification:1.0</capability>\n" + "</capabilities>\n" + "</hello>]]>]]>\n";

//Recuperação da configuração geral

public static String getRunningConfigurationRouter = "<?xml version=\"1.0\" encoding=\"UTF-8\\?>\n" + "<rpc message-id=\"101\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" + "<get-config>\n" + " <source>\n" + " <running/>\n" + " </source>\n" + " </get-config>\n" + "</rpc>]]>]]>"; }

Fonte: Elaborada pelo autor.

O diagrama contendo algumas classes utilizadas pela API NETCONF é apresentado na figura 10.

Figura 10 – Diagrama de classes da API NETCONF.

Fonte: Elaborado pelo autor.

As seções 3.3.1.2 e 3.3.1.3 tratam exclusivamente das especificações utilizadas no desenvolvimento das aplicações baseadas em web services, que utilizam como norteadores os conceitos das arquiteturas ROA e SOA e também da simulação, por meio de software, para o acesso através da CLI e do protocolo NETCONF no gerenciamento de dispositivos de rede.