Use python package instead of sshpass to execute remote commands.

Use python package instead of sshpass to execute remote commands.
Fixing this issue, by using paramiko.

Co-Authored-By: Sridhar Ramaswamy <srics.r@gmail.com>

Closes-bug: #1511658
Change-Id: I102d178b2fb1f621f2a61b26a939e69613156c19
This commit is contained in:
dharmendra 2016-06-03 17:28:33 +09:00 committed by dharmendra kushwaha
parent de6cc86ae0
commit 2259e752c8
5 changed files with 130 additions and 11 deletions

View File

@ -39,3 +39,4 @@ python-novaclient!=2.33.0,>=2.29.0 # Apache-2.0
tosca-parser>=0.5.0 # Apache-2.0
heat-translator>=0.4.0 # Apache-2.0
cryptography!=1.3.0,>=1.0 # BSD/Apache-2.0
paramiko>=2.0 # LGPLv2.1+

View File

@ -0,0 +1,107 @@
# Licensed 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.
from oslo_log import log as logging
import paramiko
from tacker.common.exceptions import NotAuthorized
LOG = logging.getLogger(__name__)
class CommandResult(object):
"""Result class contains command, stdout, stderror and return code."""
def __init__(self, cmd, stdout, stderr, return_code):
self.__cmd = cmd
self.__stdout = stdout
self.__stderr = stderr
self.__return_code = return_code
def get_command(self):
return self.__cmd
def get_stdout(self):
return self.__stdout
def get_stderr(self):
return self.__stderr
def get_return_code(self):
return self.__return_code
def __str__(self):
return "cmd: %s, stdout: %s, stderr: %s, return code: %s" \
% (self.__cmd, self.__stdout, self.__stderr, self.__return_code)
def __repr__(self):
return "cmd: %s, stdout: %s, stderr: %s, return code: %s" \
% (self.__cmd, self.__stdout, self.__stderr, self.__return_code)
class RemoteCommandExecutor(object):
"""Class to execute a command on remote location"""
def __init__(self, user, password, host, timeout=10):
self.__user = user
self.__password = password
self.__host = host
self.__paramiko_conn = None
self.__ssh = None
self.__timeout = timeout
self.__connect()
def __connect(self):
try:
self.__ssh = paramiko.SSHClient()
self.__ssh.set_missing_host_key_policy(paramiko.WarningPolicy())
self.__ssh.connect(self.__host, username=self.__user,
password=self.__password, timeout=self.__timeout)
LOG.info(_("Connected to %s") % self.__host)
except paramiko.AuthenticationException:
LOG.error(_("Authentication failed when connecting to %s")
% self.__host)
raise NotAuthorized
except paramiko.SSHException:
LOG.error(_("Could not connect to %s. Giving up") % self.__host)
raise
def close_session(self):
self.__ssh.close()
LOG.debug(_("Connection close"))
def execute_command(self, cmd, input_data=None):
try:
stdin, stdout, stderr = self.__ssh.exec_command(cmd)
if input_data:
stdin.write(input_data)
LOG.debug(_("Input data written successfuly"))
stdin.flush()
LOG.debug(_("Indput data flushed"))
stdin.channel.shutdown_write()
# NOTE (dkushwaha): There might be a case, when server can take
# too long time to write data in stdout buffer or sometimes hang
# itself, in that case readlines() will stuck for long/infinite
# time. To handle such cases, timeout logic should be introduce
# here.
cmd_out = stdout.readlines()
cmd_err = stderr.readlines()
return_code = stdout.channel.recv_exit_status()
except paramiko.SSHException:
LOG.error(_("Command execution failed at %s. Giving up")
% self.__host)
raise
result = CommandResult(cmd, cmd_out, cmd_err, return_code)
LOG.debug(_("Remote command execution result: %s"), result)
return result
def __del__(self):
self.close_session()

View File

@ -239,3 +239,7 @@ class DeviceIDNotOwnedByTenant(Conflict):
class InvalidCIDR(BadRequest):
message = _("Invalid CIDR %(input)s given as IP prefix")
class MgmtDriverException(TackerException):
message = _("VNF configuration failed")

View File

@ -19,7 +19,8 @@ from oslo_log import log as logging
from oslo_serialization import jsonutils
import yaml
from tacker.agent.linux import utils
from tacker.common import cmd_executer
from tacker.common.exceptions import MgmtDriverException
from tacker.common import log
from tacker.vm.mgmt_drivers import abstract_driver
from tacker.vm.mgmt_drivers import constants as mgmt_constants
@ -51,11 +52,16 @@ class DeviceMgmtOpenWRT(abstract_driver.DeviceMGMTAbstractDriver):
def _config_service(self, mgmt_ip_address, service, config):
user = cfg.CONF.openwrt.user
password = cfg.CONF.openwrt.password
cmd = ["sshpass", "-p", "%s" % password,
"ssh", "-o", "StrictHostKeyChecking=no",
"%s@%s" % (user, mgmt_ip_address),
"uci import %s; /etc/init.d/%s restart" % (service, service)]
utils.execute(cmd, process_input=config)
try:
cmd = "uci import %s; /etc/init.d/%s restart" % (service, service)
LOG.debug(_('execute command: %s'), (cmd))
commander = cmd_executer.RemoteCommandExecutor(user,
password,
mgmt_ip_address)
commander.execute_command(cmd, input_data=config)
except Exception as ex:
LOG.error(_("While executing command on remote: %s"), ex)
raise MgmtDriverException()
@log.log
def mgmt_call(self, plugin, context, device, kwargs):

View File

@ -25,6 +25,7 @@ from oslo_utils import excutils
from tacker.api.v1 import attributes
from tacker.common import driver_manager
from tacker.common.exceptions import MgmtDriverException
from tacker.db.vm import vm_db
from tacker.extensions import vnfm
from tacker.i18n import _LE
@ -231,11 +232,11 @@ class VNFMPlugin(vm_db.VNFMPluginDb, VNFMMgmtMixin):
new_status = constants.ACTIVE
try:
self.mgmt_call(context, device_dict, kwargs)
except Exception:
LOG.exception(_('create_device_wait'))
except MgmtDriverException:
LOG.error(_('VNF configuration failed'))
new_status = constants.ERROR
self.set_device_error_status_reason(context, device_id,
'Unable to configure VDU')
'Unable to configure VDU')
device_dict['status'] = new_status
self._create_device_status(context, device_id, new_status)
@ -307,8 +308,8 @@ class VNFMPlugin(vm_db.VNFMPluginDb, VNFMMgmtMixin):
context=context, device_id=instance_id, auth_attr=vim_auth,
region_name=region_name)
self.mgmt_call(context, device_dict, kwargs)
except Exception as e:
LOG.exception(_('_update_device_wait'))
except MgmtDriverException as e:
LOG.error(_('VNF configuration failed'))
new_status = constants.ERROR
self.set_device_error_status_reason(context, device_dict['id'],
six.text_type(e))