369 lines
13 KiB
Python
369 lines
13 KiB
Python
# Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License, version 2.0,
|
|
# as published by the Free Software Foundation.
|
|
#
|
|
# This program is also distributed with certain software (including
|
|
# but not limited to OpenSSL) that is licensed under separate terms,
|
|
# as designated in a particular file or component or in included license
|
|
# documentation. The authors of MySQL hereby grant you an additional
|
|
# permission to link the program and your derivative works with the
|
|
# separately licensed software that they have included with MySQL.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License, version 2.0, for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
#!/bin/env python
|
|
import platform
|
|
|
|
(pymajor, pyminor, pypatch) = map(int, platform.python_version_tuple())
|
|
assert pymajor == 2
|
|
|
|
import sys
|
|
|
|
import os
|
|
import os.path
|
|
|
|
tst_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
|
|
mcc_dir = os.path.dirname(tst_dir)
|
|
|
|
# Paramiko from Bazaar
|
|
sys.path += [tst_dir, mcc_dir, '/opt/csw/lib/python/site-packages' ]
|
|
|
|
import logging
|
|
if pyminor < 7:
|
|
print('Running with Python version %s',
|
|
str(platform.python_version_tuple()))
|
|
import unittest2 as unittest
|
|
else:
|
|
import unittest
|
|
|
|
utmod = unittest
|
|
|
|
import traceback
|
|
import json
|
|
import urlparse
|
|
import time
|
|
import socket
|
|
import stat
|
|
import tempfile
|
|
import platform
|
|
import time
|
|
import contextlib
|
|
|
|
import request_handler
|
|
import config_parser
|
|
import util
|
|
|
|
from util import mock_msg, is_set
|
|
from clusterhost import ABClusterHost, LocalClusterHost, produce_ABClusterHost
|
|
from remote_clusterhost import RemoteClusterHost
|
|
|
|
from paramiko import SSHClient, WarningPolicy
|
|
|
|
_ndb_config = os.path.join('..', '..', '..', 'bin', 'ndb_config')
|
|
|
|
def tst_tempify(*fs):
|
|
td = util.get_val(os.environ, 'MYSQL_TMP_DIR', tempfile.gettempdir())
|
|
#td = tempfile.mkdtemp()
|
|
return os.path.join(td, *fs)
|
|
|
|
def defdatadir():
|
|
return os.path.join(os.path.expanduser('~'), 'MySQL_Cluster', 'data')
|
|
|
|
# Default tst configuration. Can be overridden in config.json
|
|
_cfg = { 'debuglevel': 'DEBUG' }
|
|
|
|
def cfg():
|
|
return _cfg
|
|
|
|
request_handler.configdir = '..'
|
|
|
|
def_datadir = os.path.join(os.path.expanduser('~'), 'MySQL_Cluster', 'data')
|
|
|
|
|
|
def mock_ABClusterHost(hle):
|
|
return produce_ABClusterHost(hostname=hle['hostInfoRep']['host']['name'], user=util.get_val(hle, 'username'), pwd=util.get_val(hle, 'password'))
|
|
|
|
def local_ipv4_ssh_addr():
|
|
for (family, socktype, proto, canonname, sockaddr) in socket.getaddrinfo(socket.gethostname(), 22):
|
|
if family == socket.AF_INET:
|
|
return sockaddr[0]
|
|
|
|
def host_is_unreachable(hostname, port=22):
|
|
rd = util._retdict('ECONNREFUSED', True)
|
|
rd[socket.EAI_NONAME] = True
|
|
|
|
return util.try_connect(hostname, port, False, rd)
|
|
|
|
def is_local_sshd_available():
|
|
with contextlib.closing(SSHClient()) as c:
|
|
c.set_missing_host_key_policy(WarningPolicy())
|
|
#c.load_system_host_keys()
|
|
try:
|
|
c.connect(local_ipv4_ssh_addr(), password=util.get_val(os.environ, 'SSH_PWD'))
|
|
except:
|
|
logging.exception('No usable sshd on this machine: ')
|
|
return False
|
|
else:
|
|
return True
|
|
|
|
def mock_msg_as_json(cmd, body):
|
|
return json.dumps(mock_msg(cmd,body))
|
|
|
|
def json_normalize(x):
|
|
return json.loads(json.dumps(x))
|
|
|
|
class Test00Utils(utmod.TestCase):
|
|
def test_version_tuple(self):
|
|
print [ int(filter(str.isdigit, vn)) for vn in ('2', '7', '2+')]
|
|
|
|
def test_Param(self):
|
|
print util.Param({'name':'--c', 'val':'bar', 'sep':'@'})
|
|
|
|
x = util.Param('--a')
|
|
print json.dumps(x)
|
|
print util.Param('--b','foo')
|
|
print util.Param('--d','baz','#')
|
|
|
|
def test_host_is_unreachable(self):
|
|
self.assertTrue(host_is_unreachable('some_non_existent_host_name'))
|
|
|
|
@utmod.skipIf(not is_local_sshd_available(), 'FIXME: Sshd is not running by default on Windows - test some other port?')
|
|
def test_host_is_unreachable(self):
|
|
self.assertFalse(host_is_unreachable(local_ipv4_ssh_addr()))
|
|
|
|
def test_to_json(self):
|
|
obj = [ "sleep" "300" ]
|
|
json_string = json.dumps(obj)
|
|
jobj = json.loads(json_string)
|
|
self.assertEqual(obj, jobj)
|
|
|
|
class Test0ConfigIni(utmod.TestCase):
|
|
def setUp(self):
|
|
self.ini = os.path.join(tst_dir, 'example_config.ini')
|
|
self.cp = config_parser.parse_config_ini(self.ini)
|
|
|
|
def tearDown(self):
|
|
pass
|
|
|
|
def test_get_option_values(self):
|
|
self.assertEqual(config_parser.get_option_value_set(self.cp, 'HostName'), set(['siv27','siv28']))
|
|
|
|
def test_get_node_dicts(self):
|
|
print config_parser.get_node_dicts(self.cp, 0)
|
|
|
|
def test_get_configvalues(self):
|
|
print config_parser.get_configvalues(self.cp)
|
|
print config_parser.get_processes(self.cp)
|
|
|
|
def test_parse(self):
|
|
logging.debug('ex_ini as json:\n'+json.dumps(config_parser.parse_cluster_config_ini(self.ini)))
|
|
|
|
def test_parsex(self):
|
|
logging.debug('ex_ini as json:\n'+str(config_parser.parse_cluster_config_ini_x(self.ini)))
|
|
|
|
def test_write(self):
|
|
logging.debug('ex_ini write back:\n'+config_parser.write_cluster_config_ini(config_parser.parse_cluster_config_ini(self.ini)))
|
|
|
|
@utmod.skip('Need a way to specify the cluster installdir (default cluster we are part of?')
|
|
def test_ndb_config(self):
|
|
print 'ndb config as json: ', json.dumps(util.xml_to_python(self.cluster.ndb_config_xml()))
|
|
|
|
@utmod.skipIf(not is_local_sshd_available(), 'FIXME: Sshd is not running by default on Windows - test some other port?')
|
|
class Test3Ports(utmod.TestCase):
|
|
def testPortNotAvailable(self):
|
|
self.assertFalse(util.is_port_available(local_ipv4_ssh_addr(), 22))
|
|
|
|
def testPortAvailable(self):
|
|
self.assertTrue(util.is_port_available(local_ipv4_ssh_addr(), 23))
|
|
|
|
def testFindPort(self):
|
|
self.assertEqual(util.first_available_port(local_ipv4_ssh_addr(), 22, 23), 23)
|
|
|
|
def testNotFindPort(self):
|
|
self.assertRaises(util.NoPortAvailableException,
|
|
util.first_available_port, local_ipv4_ssh_addr(), 22, 22)
|
|
|
|
class _TestABClusterHost:
|
|
def setUp(self):
|
|
self.rtd = time.strftime("%Y-%m-%dT%H-%M-%S",time.localtime())+str(self.__class__.__name__)
|
|
logging.debug('rtd: '+self.rtd)
|
|
self.ch.mkdir_p(self.rtd)
|
|
|
|
def tearDown(self):
|
|
if sys.exc_info() == (None, None, None):
|
|
logging.debug('tearDown: success')
|
|
self.ch.rm_r(self.rtd)
|
|
else:
|
|
logging.debug('tearDown: failure')
|
|
logging.exception('try this...')
|
|
self.ch.drop()
|
|
|
|
@utmod.skip('FIXME: Need the path to a program that will always be available')
|
|
def test_fg_exec(self):
|
|
self.assertTrue('This program will retrieve config options for a ndb cluster' in self.ch.exec_blocking([_ndb_config, '--help']))
|
|
|
|
def test_fileops(self):
|
|
d = self.ch.path_module.join(self.rtd, 'foo', 'bar', '')
|
|
logging.debug('d='+d)
|
|
self.ch.mkdir_p(d)
|
|
bazname = self.ch.path_module.join(d, 'baz')
|
|
with self.ch.open(bazname, 'w+') as f:
|
|
f.write('Some text here...\n')
|
|
|
|
with self.ch.open(bazname) as f:
|
|
self.assertEqual(f.read(), 'Some text here...\n')
|
|
|
|
def test_stat_dir_2(self):
|
|
if not hasattr(self.ch, 'sftp'):
|
|
return
|
|
self.assertTrue(stat.S_ISDIR(self.ch.sftp.stat(self.rtd).st_mode))
|
|
|
|
@utmod.skip('Cannot create files with attributes')
|
|
def test_list_dir_no_x(self):
|
|
nox_name = self.ch.path_module.join(self.rtd, 'nox')
|
|
notmine_name = self.ch.path_module.join(self.rtd, 'not_mine')
|
|
nox_mode = self.self.ch.sftp.stat(nox_name).st_mode
|
|
notmine_mode = self.self.ch.sftp.stat(notmine_name).st_mode
|
|
|
|
self.assertTrue(stat.S_ISDIR(nox_mode))
|
|
self.assertTrue(is_set(stat.S_IMODE(nox_mode), stat.S_IXGRP))
|
|
|
|
self.assertTrue(stat.S_ISDIR(notmine_mode))
|
|
self.assertTrue(is_set(stat.S_IMODE(notmine_mode), stat.S_IXOTH))
|
|
|
|
logging.debug('listdir(nox)='+str(self.self.ch.list_dir(nox_name)))
|
|
logging.debug('listdir(notmine)='+str(self.self.ch.list_dir(notmine_name)))
|
|
|
|
def test_mkdir_p(self):
|
|
somedir_name = self.ch.path_module.join(self.rtd, 'some_dir')
|
|
logging.debug('somedir_name'+somedir_name)
|
|
self.ch.mkdir_p(somedir_name)
|
|
|
|
def test_hostInfo(self):
|
|
self.assertGreater(self.ch.hostInfo.ram, 1000)
|
|
self.assertGreater(self.ch.hostInfo.cores, 0)
|
|
self.assertIsNotNone(self.ch.hostInfo.homedir)
|
|
|
|
|
|
class Test4LocalClusterHost(utmod.TestCase, _TestABClusterHost):
|
|
def setUp(self):
|
|
self.ch = LocalClusterHost('localhost')
|
|
_TestABClusterHost.setUp(self)
|
|
|
|
def tearDown(self):
|
|
_TestABClusterHost.tearDown(self)
|
|
|
|
@utmod.skipIf(not is_local_sshd_available(), 'No suitable local sshd')
|
|
class Test5RemoteClusterHost(utmod.TestCase, _TestABClusterHost):
|
|
def setUp(self):
|
|
self.ch = RemoteClusterHost(local_ipv4_ssh_addr(), password=util.get_val(os.environ, 'SSH_PWD'))
|
|
_TestABClusterHost.setUp(self)
|
|
|
|
def tearDown(self):
|
|
_TestABClusterHost.tearDown(self)
|
|
|
|
def test_copy_file(self):
|
|
if not hasattr(self.ch, 'sftp'):
|
|
return
|
|
|
|
content = 'Some text here\nSome more text on another line\n'
|
|
(lh, lname) = tempfile.mkstemp()
|
|
logging.debug('lname: '+lname)
|
|
try:
|
|
os.write(lh, content)
|
|
|
|
# Note! Might not work with a genuine remote host, as rex.name
|
|
# might not be creatable there
|
|
(rh, rname) = tempfile.mkstemp()
|
|
os.close(rh)
|
|
# Fixme! Assumes SFTP home is C: (More commonly HOMEPATH or USERPROFILE?
|
|
rname = os.path.basename(rname)
|
|
self.ch.sftp.put(lname, rname)
|
|
try:
|
|
(llh, llname) = tempfile.mkstemp()
|
|
os.close(llh)
|
|
self.ch.sftp.get(rname, llname)
|
|
try:
|
|
with open(llname) as llh:
|
|
self.assertEqual(content, llh.read())
|
|
finally:
|
|
os.remove(llname)
|
|
finally:
|
|
self.ch.sftp.remove(rname)
|
|
finally:
|
|
os.close(lh)
|
|
os.remove(lname)
|
|
|
|
def test_mkdir(self):
|
|
if not hasattr(self.ch, 'sftp'):
|
|
return
|
|
(h, n) = tempfile.mkstemp()
|
|
os.write(h, "Some text here\nSome more text on another line\n")
|
|
os.close(h)
|
|
|
|
some_new_dir_name = self.ch.path_module.join(self.rtd, 'some_new_dir')
|
|
self.ch.sftp.mkdir(some_new_dir_name)
|
|
tmpex_name = self.ch.path_module.join(some_new_dir_name, 'example.txt')
|
|
self.ch.sftp.put(n, tmpex_name)
|
|
os.remove(n)
|
|
|
|
def test_stat_dir(self):
|
|
if not hasattr(self.ch, 'sftp'):
|
|
return
|
|
self.assertEqual(stat.S_IFMT(self.ch.sftp.stat(self.rtd).st_mode), stat.S_IFDIR)
|
|
|
|
|
|
class Test6RequestHandler(utmod.TestCase):
|
|
def setUp(self):
|
|
self.ssh = {'user': None, 'pwd': None }
|
|
|
|
def tearDown(self):
|
|
pass
|
|
|
|
|
|
def test_hostInfoReq(self):
|
|
json_str = mock_msg_as_json('hostInfoReq', {'ssh': self.ssh,
|
|
'hostName': 'localhost'})
|
|
print 'hostInfoReq: '+json_str
|
|
print request_handler.handle_req(json.loads(json_str))
|
|
|
|
def test_createFileReq(self):
|
|
json_str = mock_msg_as_json('createFileReq',
|
|
{'ssh': self.ssh, 'file': {'hostName': 'localhost', 'path': tempfile.gettempdir(), 'name': 'foobar'},
|
|
'contentString': 'a=0\nb=1\n', 'contents': {'params': {'sep': None, 'para': [{'name': None, 'sep': None, 'val': None}]}}})
|
|
print json_str
|
|
try:
|
|
print request_handler.handle_req(json.loads(json_str))
|
|
finally:
|
|
try:
|
|
os.remove(os.path.join(tempfile.gettempdir(),'foobar'))
|
|
except:
|
|
pass
|
|
|
|
if __name__ == '__main__':
|
|
if cfg()['debuglevel'] is not None:
|
|
import logging
|
|
fmt = '%(asctime)s: %(levelname)s [%(funcName)s;%(filename)s:%(lineno)d]: %(message)s '
|
|
logging.basicConfig(level=getattr(logging, cfg()['debuglevel']), format=fmt)
|
|
|
|
try:
|
|
import xmlrunner2
|
|
except:
|
|
traceback.print_exc()
|
|
assert(False)
|
|
utmod.main(argv=sys.argv)
|
|
else:
|
|
if os.environ.has_key('XMLRUNNER'):
|
|
utmod.main(argv=sys.argv, testRunner=xmlrunner2.XMLTestRunner)
|
|
else:
|
|
utmod.main(argv=sys.argv)
|