| // |
| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| // |
| |
| package com.cloud.utils.cisco.n1kv.vsm; |
| |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.util.List; |
| |
| import org.apache.log4j.Logger; |
| |
| import com.trilead.ssh2.Connection; |
| import com.trilead.ssh2.Session; |
| |
| import com.cloud.utils.Pair; |
| import com.cloud.utils.cisco.n1kv.vsm.VsmCommand.BindingType; |
| import com.cloud.utils.cisco.n1kv.vsm.VsmCommand.PortProfileType; |
| import com.cloud.utils.cisco.n1kv.vsm.VsmCommand.SwitchPortMode; |
| import com.cloud.utils.exception.CloudRuntimeException; |
| import com.cloud.utils.ssh.SSHCmdHelper; |
| |
| public class NetconfHelper { |
| private static final Logger s_logger = Logger.getLogger(NetconfHelper.class); |
| |
| private static final String SSH_NETCONF_TERMINATOR = "]]>]]>"; |
| |
| private Connection _connection; |
| |
| private Session _session; |
| |
| public NetconfHelper(String ip, String username, String password) throws CloudRuntimeException { |
| _connection = SSHCmdHelper.acquireAuthorizedConnection(ip, username, password); |
| if (_connection == null) { |
| throw new CloudRuntimeException("Error opening ssh connection."); |
| } |
| |
| try { |
| _session = _connection.openSession(); |
| _session.startSubSystem("xmlagent"); |
| exchangeHello(); |
| } catch (final Exception e) { |
| disconnect(); |
| s_logger.error("Failed to connect to device SSH server: " + e.getMessage()); |
| throw new CloudRuntimeException("Failed to connect to SSH server: " + _connection.getHostname()); |
| } |
| } |
| |
| public void disconnect() { |
| if (_session != null) { |
| _session.close(); |
| } |
| SSHCmdHelper.releaseSshConnection(_connection); |
| } |
| |
| public void queryStatus() throws CloudRuntimeException { |
| // This command is used to query the server status. |
| String status = |
| "<?xml version=\"1.0\"?>" + "<nc:rpc message-id=\"1\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0" + "\"xmlns=\"http://www.cisco.com/nxos:1.0:xml\">" + |
| " <nc:get>" + " <nc:filter type=\"subtree\">" + " <show>" + " <xml>" + " <server>" + " <status/>" + |
| " </server>" + " </xml>" + " </show>" + " </nc:filter>" + " </nc:get>" + "</nc:rpc>" + SSH_NETCONF_TERMINATOR; |
| send(status); |
| // parse the rpc reply. |
| parseOkReply(receive()); |
| } |
| |
| public void addPortProfile(String name, PortProfileType type, BindingType binding, SwitchPortMode mode, int vlanid, String vdc, String espName) |
| throws CloudRuntimeException { |
| String command = VsmCommand.getAddPortProfile(name, type, binding, mode, vlanid, vdc, espName); |
| if (command != null) { |
| command = command.concat(SSH_NETCONF_TERMINATOR); |
| parseOkReply(sendAndReceive(command)); |
| } else { |
| throw new CloudRuntimeException("Error generating rpc request for adding port profile."); |
| } |
| } |
| |
| public void addPortProfile(String name, PortProfileType type, BindingType binding, SwitchPortMode mode, int vlanid) throws CloudRuntimeException { |
| String command = VsmCommand.getAddPortProfile(name, type, binding, mode, vlanid); |
| if (command != null) { |
| command = command.concat(SSH_NETCONF_TERMINATOR); |
| parseOkReply(sendAndReceive(command)); |
| } else { |
| throw new CloudRuntimeException("Error generating rpc request for adding port profile."); |
| } |
| } |
| |
| public void updatePortProfile(String name, SwitchPortMode mode, List<Pair<VsmCommand.OperationType, String>> params) throws CloudRuntimeException { |
| String command = VsmCommand.getUpdatePortProfile(name, mode, params); |
| if (command != null) { |
| command = command.concat(SSH_NETCONF_TERMINATOR); |
| parseOkReply(sendAndReceive(command)); |
| } else { |
| throw new CloudRuntimeException("Error generating rpc request for updating port profile."); |
| } |
| } |
| |
| public void deletePortProfile(String name) throws CloudRuntimeException { |
| String command = VsmCommand.getDeletePortProfile(name); |
| if (command != null) { |
| command = command.concat(SSH_NETCONF_TERMINATOR); |
| parseOkReply(sendAndReceive(command)); |
| } else { |
| throw new CloudRuntimeException("Error generating rpc request for deleting port profile."); |
| } |
| } |
| |
| public void addPolicyMap(String name, int averageRate, int maxRate, int burstRate) throws CloudRuntimeException { |
| String command = VsmCommand.getAddPolicyMap(name, averageRate, maxRate, burstRate); |
| if (command != null) { |
| command = command.concat(SSH_NETCONF_TERMINATOR); |
| parseOkReply(sendAndReceive(command)); |
| } else { |
| throw new CloudRuntimeException("Error generating rpc request for adding/updating policy map."); |
| } |
| } |
| |
| public void deletePolicyMap(String name) throws CloudRuntimeException { |
| String command = VsmCommand.getDeletePolicyMap(name); |
| if (command != null) { |
| command = command.concat(SSH_NETCONF_TERMINATOR); |
| parseOkReply(sendAndReceive(command)); |
| } else { |
| throw new CloudRuntimeException("Error generating rpc request for deleting policy map."); |
| } |
| } |
| |
| public void updatePolicyMap(String name, int averageRate, int maxRate, int burstRate) throws CloudRuntimeException { |
| // Add and update of policy map work in the exact same way. |
| addPolicyMap(name, averageRate, maxRate, burstRate); |
| } |
| |
| public void attachServicePolicy(String policyMap, String portProfile) throws CloudRuntimeException { |
| String command = VsmCommand.getServicePolicy(policyMap, portProfile, true); |
| if (command != null) { |
| command = command.concat(SSH_NETCONF_TERMINATOR); |
| parseOkReply(sendAndReceive(command)); |
| } else { |
| throw new CloudRuntimeException("Error generating rpc request for adding policy map."); |
| } |
| } |
| |
| public void detachServicePolicy(String policyMap, String portProfile) throws CloudRuntimeException { |
| String command = VsmCommand.getServicePolicy(policyMap, portProfile, false); |
| if (command != null) { |
| command = command.concat(SSH_NETCONF_TERMINATOR); |
| parseOkReply(sendAndReceive(command)); |
| } else { |
| throw new CloudRuntimeException("Error generating rpc request for removing policy map."); |
| } |
| } |
| |
| public void addVServiceNode(String vlanId, String ipAddr) throws CloudRuntimeException { |
| String command = VsmCommand.getVServiceNode(vlanId, ipAddr); |
| if (command != null) { |
| command = command.concat(SSH_NETCONF_TERMINATOR); |
| parseOkReply(sendAndReceive(command)); |
| } else { |
| throw new CloudRuntimeException("Error generating rpc request for adding vservice node for vlan " + vlanId); |
| } |
| } |
| |
| public PortProfile getPortProfileByName(String name) throws CloudRuntimeException { |
| String command = VsmCommand.getPortProfile(name); |
| if (command != null) { |
| command = command.concat(SSH_NETCONF_TERMINATOR); |
| String received = sendAndReceive(command); |
| VsmPortProfileResponse response = new VsmPortProfileResponse(received.trim()); |
| if (!response.isResponseOk()) { |
| throw new CloudRuntimeException(response.toString()); |
| } else { |
| return response.getPortProfile(); |
| } |
| } else { |
| throw new CloudRuntimeException("Error generating rpc request for getting port profile."); |
| } |
| } |
| |
| public PolicyMap getPolicyMapByName(String name) throws CloudRuntimeException { |
| String command = VsmCommand.getPolicyMap(name); |
| if (command != null) { |
| command = command.concat(SSH_NETCONF_TERMINATOR); |
| String received = sendAndReceive(command); |
| VsmPolicyMapResponse response = new VsmPolicyMapResponse(received.trim()); |
| if (!response.isResponseOk()) { |
| throw new CloudRuntimeException(response.toString()); |
| } else { |
| return response.getPolicyMap(); |
| } |
| } else { |
| throw new CloudRuntimeException("Error generating rpc request for getting policy map."); |
| } |
| } |
| |
| private void exchangeHello() { |
| String ack = receive(); |
| String hello = VsmCommand.getHello() + SSH_NETCONF_TERMINATOR; |
| send(hello); |
| } |
| |
| private String sendAndReceive(String command) { |
| String received; |
| synchronized (NetconfHelper.class) { |
| send(command); |
| received = receive(); |
| } |
| return received; |
| } |
| |
| private void send(String message) { |
| try { |
| OutputStream outputStream = _session.getStdin(); |
| outputStream.write(message.getBytes()); |
| outputStream.flush(); |
| } catch (Exception e) { |
| s_logger.error("Failed to send message: " + e.getMessage()); |
| throw new CloudRuntimeException("Failed to send message: " + e.getMessage()); |
| } |
| } |
| |
| private String receive() { |
| String response = new String(""); |
| InputStream inputStream = _session.getStdout(); |
| |
| try { |
| Delimiter delimiter = new Delimiter(); |
| byte[] buffer = new byte[1024]; |
| int count = 0; |
| |
| // Read the input stream till we find the end sequence ']]>]]>'. |
| while (true) { |
| int data = inputStream.read(); |
| if (data != -1) { |
| byte[] dataStream = delimiter.parse(data); |
| if (delimiter.endReached()) { |
| response += new String(buffer, 0, count); |
| break; |
| } |
| |
| if (dataStream != null) { |
| for (int i = 0; i < dataStream.length; i++) { |
| buffer[count] = dataStream[i]; |
| count++; |
| if (count == 1024) { |
| response += new String(buffer, 0, count); |
| count = 0; |
| } |
| } |
| } |
| } else { |
| break; |
| } |
| } |
| } catch (final Exception e) { |
| throw new CloudRuntimeException("Error occurred while reading from the stream: " + e.getMessage()); |
| } |
| |
| return response; |
| } |
| |
| private void parseOkReply(String reply) throws CloudRuntimeException { |
| VsmOkResponse response = new VsmOkResponse(reply.trim()); |
| if (!response.isResponseOk()) { |
| throw new CloudRuntimeException(response.toString()); |
| } |
| } |
| |
| private static class Delimiter { |
| private boolean _endReached = false; |
| |
| // Used to accumulate response read while searching for end of response. |
| private byte[] _gatherResponse = new byte[6]; |
| |
| // Index into number of bytes read. |
| private int _offset = 0; |
| |
| // True if ']]>]]>' detected. |
| boolean endReached() { |
| return _endReached; |
| } |
| |
| // Parses the input stream and checks if end sequence is reached. |
| byte[] parse(int input) throws RuntimeException { |
| boolean collect = false; |
| byte[] streamRead = null; |
| |
| // Check if end sequence matched. |
| switch (_offset) { |
| case 0: |
| if (input == ']') { |
| collect = true; |
| } |
| break; |
| case 1: |
| if (input == ']') { |
| collect = true; |
| } |
| break; |
| case 2: |
| if (input == '>') { |
| collect = true; |
| } |
| break; |
| case 3: |
| if (input == ']') { |
| collect = true; |
| } |
| break; |
| case 4: |
| if (input == ']') { |
| collect = true; |
| } |
| break; |
| case 5: |
| if (input == '>') { |
| collect = true; |
| _endReached = true; |
| } |
| break; |
| default: |
| throw new RuntimeException("Invalid index value: " + _offset); |
| } |
| |
| if (collect) { |
| _gatherResponse[_offset++] = (byte)input; |
| } else { |
| // End sequence not yet reached. Return the stream of bytes collected so far. |
| streamRead = new byte[_offset + 1]; |
| for (int index = 0; index < _offset; ++index) { |
| streamRead[index] = _gatherResponse[index]; |
| } |
| |
| streamRead[_offset] = (byte)input; |
| _offset = 0; |
| } |
| |
| return streamRead; |
| } |
| } |
| } |