blob: 49860a14e3b9ba8979a5263167bd3aff36c1c972 [file] [log] [blame]
# 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.
# libcloud.org 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.
"""
Wraps multiple ways to communicate over SSH
"""
have_paramiko = False
try:
import paramiko
have_paramiko = True
except ImportError:
pass
from os.path import split as psplit
class BaseSSHClient(object):
"""
Base class representing a connection over SSH/SCP to a remote node.
"""
def __init__(self, hostname, port=22, username='root', password=None, key=None):
"""
@type hostname: C{str}
@keyword hostname: Hostname or IP address to connect to.
@type port: C{int}
@keyword port: TCP port to communicate on, defaults to 22.
@type username: C{str}
@keyword username: Username to use, defaults to root.
@type password: C{str}
@keyword password: Password to authenticate with.
@type key: C{list}
@keyword key: Private SSH keys to authenticate with.
"""
self.hostname = hostname
self.port = port
self.username = username
self.password = password
self.key = key
def connect(self):
"""
Connect to the remote node over SSH.
@return: C{bool}
"""
raise NotImplementedError, \
'connect not implemented for this ssh client'
def put(self, path, contents=None, chmod=None):
"""
Upload a file to the remote node.
@type path: C{str}
@keyword path: File path on the remote node.
@type contents: C{str}
@keyword contents: File Contents.
@type chmod: C{int}
@keyword chmod: chmod file to this after creation.
"""
raise NotImplementedError, \
'put not implemented for this ssh client'
def delete(self, path):
"""
Delete/Unlink a file on the remote node.
@type path: C{str}
@keyword path: File path on the remote node.
"""
raise NotImplementedError, \
'delete not implemented for this ssh client'
def run(self, cmd):
"""
Run a command on a remote node.
@type cmd: C{str}
@keyword cmd: Command to run.
"""
raise NotImplementedError, \
'run not implemented for this ssh client'
def close(self):
"""
Shutdown connection to the remote node.
"""
raise NotImplementedError, \
'close not implemented for this ssh client'
class ParamikoSSHClient(BaseSSHClient):
"""
A SSH Client powered by Paramiko.
"""
def __init__(self, hostname, port=22, username='root', password=None, key=None):
super(ParamikoSSHClient, self).__init__(hostname, port, username, password, key)
self.client = paramiko.SSHClient()
self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
def connect(self):
conninfo = {'hostname': self.hostname,
'port': self.port,
'username': self.username,
'password': self.password,
'allow_agent': False,
'look_for_keys': False}
self.client.connect(**conninfo)
return True
def put(self, path, contents=None, chmod=None):
sftp = self.client.open_sftp()
# less than ideal, but we need to mkdir stuff otherwise file() fails
head, tail = psplit(path)
if path[0] == "/":
sftp.chdir("/")
for part in head.split("/"):
if part != "":
try:
sftp.mkdir(part)
except IOError:
# so, there doesn't seem to be a way to
# catch EEXIST consistently *sigh*
pass
sftp.chdir(part)
ak = sftp.file(tail, mode='w')
ak.write(contents)
if chmod is not None:
ak.chmod(chmod)
ak.close()
sftp.close()
def delete(self, path):
sftp = self.client.open_sftp()
sftp.unlink(path)
sftp.close()
def run(self, cmd):
stdin, stdout, stderr = self.client.exec_command(cmd)
stdin.close()
so = stdout.read()
se = stderr.read()
return [so, se]
def close(self):
self.client.close()
class ShellOutSSHClient(BaseSSHClient):
# TODO: write this one
pass
SSHClient = ParamikoSSHClient
if not have_paramiko:
SSHClient = ShellOutSSHClient