diff --git a/kmip/logconfig.ini b/kmip/logconfig.ini index 32cf501..190d62c 100644 --- a/kmip/logconfig.ini +++ b/kmip/logconfig.ini @@ -18,4 +18,4 @@ formatter=simpleFormatter args=(sys.stdout,) [formatter_simpleFormatter] -format=%(asctime)s - %(name)s - %(levelname)s - %(message)s \ No newline at end of file +format=%(asctime)s - %(name)s - %(levelname)s - %(message)s diff --git a/kmip/pykmip.conf b/kmip/pykmip.conf index 783f864..2dae375 100644 --- a/kmip/pykmip.conf +++ b/kmip/pykmip.conf @@ -5,7 +5,7 @@ keyfile=None certfile=None cert_reqs=CERT_REQUIRED ssl_version=PROTOCOL_SSLv23 -ca_certs=../demos/certs/server.crt +ca_certs=./demos/certs/server.crt do_handshake_on_connect=True suppress_ragged_eofs=True username=None @@ -15,8 +15,8 @@ timeout=30 [server] host=127.0.0.1 port=5696 -keyfile=../demos/certs/server.key -certfile=../demos/certs/server.crt +keyfile=./demos/certs/server.key +certfile=./demos/certs/server.crt cert_reqs=CERT_NONE ssl_version=PROTOCOL_SSLv23 ca_certs=None diff --git a/kmip/services/kmip_client.py b/kmip/services/kmip_client.py index 0cfbf9d..94f9e1e 100644 --- a/kmip/services/kmip_client.py +++ b/kmip/services/kmip_client.py @@ -76,7 +76,8 @@ CONFIG_FILE = os.path.normpath(os.path.join(FILE_PATH, '../kmipconfig.ini')) class KMIPProxy(KMIP): - def __init__(self, host=None, port=None, keyfile=None, certfile=None, + def __init__(self, host=None, port=None, keyfile=None, + certfile=None, cert_reqs=None, ssl_version=None, ca_certs=None, do_handshake_on_connect=None, suppress_ragged_eofs=None, @@ -193,7 +194,6 @@ class KMIPProxy(KMIP): self.is_authentication_suite_supported(authentication_suite)) def open(self): - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.logger.debug("KMIPProxy keyfile: {0}".format(self.keyfile)) self.logger.debug("KMIPProxy certfile: {0}".format(self.certfile)) @@ -209,6 +209,23 @@ class KMIPProxy(KMIP): self.logger.debug("KMIPProxy suppress_ragged_eofs: {0}".format( self.suppress_ragged_eofs)) + for host in self.host_list: + self.host = host + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self._create_socket(sock) + self.protocol = KMIPProtocol(self.socket) + try: + self.socket.connect((self.host, self.port)) + except Exception as e: + self.logger.error("An error occurred while connecting to " + "appliance " + self.host) + self.socket.close() + self.socket = None + else: + return + raise e + + def _create_socket(self, sock): self.socket = ssl.wrap_socket( sock, keyfile=self.keyfile, @@ -218,16 +235,8 @@ class KMIPProxy(KMIP): ca_certs=self.ca_certs, do_handshake_on_connect=self.do_handshake_on_connect, suppress_ragged_eofs=self.suppress_ragged_eofs) - self.protocol = KMIPProtocol(self.socket) - self.socket.settimeout(self.timeout) - try: - self.socket.connect((self.host, self.port)) - except socket.timeout as e: - self.logger.error("timeout occurred while connecting to appliance") - raise e - def __del__(self): # Close the socket properly, helpful in case close() is not called. self.close() @@ -881,9 +890,14 @@ class KMIPProxy(KMIP): username, password, timeout): conf = ConfigHelper() - self.host = conf.get_valid_value( + # TODO: set this to a host list + self.host_list_str = conf.get_valid_value( host, self.config, 'host', conf.DEFAULT_HOST) + self.host_list = self._build_host_list(self.host_list_str) + + self.host = self.host_list[0] + self.port = int(conf.get_valid_value( port, self.config, 'port', conf.DEFAULT_PORT)) @@ -922,11 +936,27 @@ class KMIPProxy(KMIP): self.password = conf.get_valid_value( password, self.config, 'password', conf.DEFAULT_PASSWORD) - self.timeout = conf.get_valid_value( - timeout, self.config, 'timeout', conf.DEFAULT_TIMEOUT) + self.timeout = int(conf.get_valid_value( + timeout, self.config, 'timeout', conf.DEFAULT_TIMEOUT)) if self.timeout < 0: self.logger.warning( "Negative timeout value specified, " "resetting to safe default of {0} seconds".format( conf.DEFAULT_TIMEOUT)) self.timeout = conf.DEFAULT_TIMEOUT + + def _build_host_list(self, host_list_str): + ''' + This internal function takes the host string from the config file + and turns it into a list + :return: LIST host list + ''' + + host_list = [] + if isinstance(host_list_str, str): + host_list = host_list_str.replace(' ', '').split(',') + else: + raise TypeError("Unrecognized variable type provided for host " + "list string. 'String' type expected but '" + + str(type(host_list_str)) + "' received") + return host_list diff --git a/kmip/tests/unit/services/test_kmip_client.py b/kmip/tests/unit/services/test_kmip_client.py index 082e10d..3f4cf48 100644 --- a/kmip/tests/unit/services/test_kmip_client.py +++ b/kmip/tests/unit/services/test_kmip_client.py @@ -61,6 +61,10 @@ from kmip.services.results import RekeyKeyPairResult import kmip.core.utils as utils +import mock + +import socket + class TestKMIPClient(TestCase): @@ -501,6 +505,62 @@ class TestKMIPClient(TestCase): self.assertEqual(uid, result.uid) self.assertEqual(names, result.names) + def test_host_list_import_string(self): + """ + This test verifies that the client can process a string with + multiple IP addresses specified in it. It also tests that + unnecessary spaces are ignored. + """ + + host_list_string = '127.0.0.1,127.0.0.3, 127.0.0.5' + host_list_expected = ['127.0.0.1', '127.0.0.3', '127.0.0.5'] + + self.client._set_variables(host=host_list_string, + port=None, keyfile=None, certfile=None, + cert_reqs=None, ssl_version=None, + ca_certs=None, + do_handshake_on_connect=False, + suppress_ragged_eofs=None, username=None, + password=None, timeout=None) + self.assertEqual(host_list_expected, self.client.host_list) + + def test_host_is_invalid_input(self): + """ + This test verifies that invalid values are not processed when + setting the client object parameters + """ + host = 1337 + expected_error = TypeError + + kwargs = {'host': host, 'port': None, 'keyfile': None, + 'certfile': None, 'cert_reqs': None, 'ssl_version': None, + 'ca_certs': None, 'do_handshake_on_connect': False, + 'suppress_ragged_eofs': None, 'username': None, + 'password': None, 'timeout': None} + + self.assertRaises(expected_error, self.client._set_variables, + **kwargs) + + @mock.patch('socket.socket.connect') + @mock.patch('ssl.SSLSocket.gettimeout') + def test_timeout_all_hosts(self, mock_ssl_timeout, mock_connect_return): + """ + This test verifies that the client will throw an exception if no + hosts are available for connection. + """ + + mock_ssl_timeout.return_value = 1 + mock_connect_return.return_value = socket.timeout + try: + self.client.open() + except Exception as e: + # TODO: once the exception is properly defined in the + # kmip_client.py file this test needs to change to reflect that. + self.assertIsInstance(e, Exception) + self.client.close() + else: + self.client.close() + class TestClientProfileInformation(TestCase): """