diff options
author | Davanum Srinivas <dims@linux.vnet.ibm.com> | 2014-09-09 15:01:33 -0400 |
---|---|---|
committer | Davanum Srinivas <dims@linux.vnet.ibm.com> | 2014-09-12 07:24:51 -0400 |
commit | 4bd0b4cf7ab45f121aaedfdf55c77aa72bce8b38 (patch) | |
tree | 2575b9426104129b6fbe967a25065003af2d1e68 | |
parent | 5571e9f43597aa7257829f6e4bf6544abfa88880 (diff) | |
download | oslo-vmware-4bd0b4cf7ab45f121aaedfdf55c77aa72bce8b38.tar.gz |
VMware: Enable vCenter SSL certificate validation
During https/ssl communication the server certificate of
a vCenter is not validated during https handshake process.
This patch will enable the certificate validation against root
certificate of the vCenter to validate the identity of that vCenter.
Nova, Cinder etc will need to specify the cacert parameter
for this to take effect.
Ported from Id: Ic24951630698068321bb62d3098de8344a35c5d0
Closes-Bug: #1276207
Co-Authored-By: Johnson koil raj <johnson.raj@hp.com>
Change-Id: I422e6d4df4e392334c0ce47e6f5b017a8c95f61a
-rw-r--r-- | oslo/vmware/api.py | 16 | ||||
-rw-r--r-- | oslo/vmware/pbm.py | 8 | ||||
-rw-r--r-- | oslo/vmware/service.py | 33 | ||||
-rw-r--r-- | oslo/vmware/vim.py | 8 | ||||
-rw-r--r-- | requirements.txt | 1 | ||||
-rw-r--r-- | tests/test_api.py | 9 | ||||
-rw-r--r-- | tests/test_service.py | 8 |
7 files changed, 66 insertions, 17 deletions
diff --git a/oslo/vmware/api.py b/oslo/vmware/api.py index 5e65786..82ecddf 100644 --- a/oslo/vmware/api.py +++ b/oslo/vmware/api.py @@ -143,7 +143,7 @@ class VMwareAPISession(object): def __init__(self, host, server_username, server_password, api_retry_count, task_poll_interval, scheme='https', create_session=True, wsdl_loc=None, pbm_wsdl_loc=None, - port=443): + port=443, cacert=None, insecure=True): """Initializes the API session with given parameters. :param host: ESX/VC server IP address or host name @@ -159,6 +159,10 @@ class VMwareAPISession(object): instance creation :param wsdl_loc: VIM API WSDL file location :param pbm_wsdl_loc: PBM service WSDL file location + :param cacert: Specify a CA bundle file to use in verifying a + TLS (https) server certificate. + :param insecure: Verify HTTPS connections using system certificates, + used only if cacert is not specified :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException """ @@ -175,6 +179,8 @@ class VMwareAPISession(object): self._session_username = None self._vim = None self._pbm = None + self._cacert = cacert + self._insecure = insecure if create_session: self._create_session() @@ -184,7 +190,9 @@ class VMwareAPISession(object): self._vim = vim.Vim(protocol=self._scheme, host=self._host, port=self._port, - wsdl_url=self._vim_wsdl_loc) + wsdl_url=self._vim_wsdl_loc, + cacert=self._cacert, + insecure=self._insecure) return self._vim @property @@ -193,7 +201,9 @@ class VMwareAPISession(object): self._pbm = pbm.Pbm(protocol=self._scheme, host=self._host, port=self._port, - wsdl_url=self._pbm_wsdl_loc) + wsdl_url=self._pbm_wsdl_loc, + cacert=self._cacert, + insecure=self._insecure) if self._session_id: # To handle the case where pbm property is accessed after # session creation. If pbm property is accessed before session diff --git a/oslo/vmware/pbm.py b/oslo/vmware/pbm.py index 46c9784..005806b 100644 --- a/oslo/vmware/pbm.py +++ b/oslo/vmware/pbm.py @@ -41,17 +41,21 @@ class Pbm(service.Service): """Service class that provides access to the Storage Policy API.""" def __init__(self, protocol='https', host='localhost', port=443, - wsdl_url=None): + wsdl_url=None, cacert=None, insecure=True): """Constructs a PBM service client object. :param protocol: http or https :param host: server IP address or host name :param port: port for connection :param wsdl_url: PBM WSDL url + :param cacert: Specify a CA bundle file to use in verifying a + TLS (https) server certificate. + :param insecure: Verify HTTPS connections using system certificates, + used only if cacert is not specified """ base_url = service.Service.build_base_url(protocol, host, port) soap_url = base_url + '/pbm' - super(Pbm, self).__init__(wsdl_url, soap_url) + super(Pbm, self).__init__(wsdl_url, soap_url, cacert, insecure) def set_soap_cookie(self, cookie): """Set the specified vCenter session cookie in the SOAP header diff --git a/oslo/vmware/service.py b/oslo/vmware/service.py index 3b7583f..92a678f 100644 --- a/oslo/vmware/service.py +++ b/oslo/vmware/service.py @@ -19,11 +19,12 @@ Common classes that provide access to vSphere services. import httplib import logging -import urllib2 import netaddr +import requests import six import suds +from suds import transport from oslo.vmware._i18n import _ from oslo.vmware import exceptions @@ -68,17 +69,41 @@ class ServiceMessagePlugin(suds.plugin.MessagePlugin): context.envelope.walk(self.add_attribute_for_value) +class RequestsTransport(transport.Transport): + def __init__(self, cacert=None, insecure=True): + transport.Transport.__init__(self) + # insecure flag is used only if cacert is not + # specified. + self.verify = cacert if cacert else not insecure + self.session = requests.Session() + self.cookiejar = self.session.cookies + + def open(self, request): + resp = self.session.get(request.url, verify=self.verify) + return six.StringIO(resp.content) + + def send(self, request): + resp = self.session.post(request.url, + data=request.message, + headers=request.headers, + verify=self.verify) + return transport.Reply(resp.status_code, resp.headers, resp.content) + + class Service(object): """Base class containing common functionality for invoking vSphere services """ - def __init__(self, wsdl_url=None, soap_url=None): + def __init__(self, wsdl_url=None, soap_url=None, + cacert=None, insecure=True): self.wsdl_url = wsdl_url self.soap_url = soap_url LOG.debug("Creating suds client with soap_url='%s' and wsdl_url='%s'", self.soap_url, self.wsdl_url) + transport = RequestsTransport(cacert, insecure) self.client = suds.client.Client(self.wsdl_url, + transport=transport, location=self.soap_url, plugins=[ServiceMessagePlugin()], cache=suds.cache.NoCache()) @@ -206,9 +231,9 @@ class Service(object): raise exceptions.VimSessionOverLoadException( _("httplib error in %s.") % attr_name, excep) - except (urllib2.URLError, urllib2.HTTPError) as excep: + except requests.RequestException as excep: raise exceptions.VimConnectionException( - _("urllib2 error in %s.") % attr_name, excep) + _("requests error in %s.") % attr_name, excep) except Exception as excep: # TODO(vbala) should catch specific exceptions and raise diff --git a/oslo/vmware/vim.py b/oslo/vmware/vim.py index a9074cf..95a2543 100644 --- a/oslo/vmware/vim.py +++ b/oslo/vmware/vim.py @@ -20,13 +20,17 @@ class Vim(service.Service): """Service class that provides access to the VIM API.""" def __init__(self, protocol='https', host='localhost', port=None, - wsdl_url=None): + wsdl_url=None, cacert=None, insecure=True): """Constructs a VIM service client object. :param protocol: http or https :param host: server IP address or host name :param port: port for connection :param wsdl_url: VIM WSDL url + :param cacert: Specify a CA bundle file to use in verifying a + TLS (https) server certificate. + :param insecure: Verify HTTPS connections using system certificates, + used only if cacert is not specified :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException, VimConnectionException """ @@ -34,7 +38,7 @@ class Vim(service.Service): soap_url = base_url + '/sdk' if wsdl_url is None: wsdl_url = soap_url + '/vimService.wsdl' - super(Vim, self).__init__(wsdl_url, soap_url) + super(Vim, self).__init__(wsdl_url, soap_url, cacert, insecure) def retrieve_service_content(self): return self.RetrieveServiceContent(service.SERVICE_INSTANCE) diff --git a/requirements.txt b/requirements.txt index 57d2aef..55e5eac 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,3 +19,4 @@ PyYAML>=3.1.0 suds>=0.4 eventlet>=0.15.1 +requests>=1.2.1,!=2.4.0 diff --git a/tests/test_api.py b/tests/test_api.py index 6c02051..99730c9 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -108,6 +108,7 @@ class VMwareAPISessionTest(base.TestCase): self.addCleanup(patcher.stop) self.VimMock = patcher.start() self.VimMock.side_effect = lambda *args, **kw: mock.MagicMock() + self.cert_mock = mock.Mock() def _create_api_session(self, _create_session, retry_count=10, task_poll_interval=1): @@ -118,7 +119,9 @@ class VMwareAPISessionTest(base.TestCase): task_poll_interval, 'https', _create_session, - port=VMwareAPISessionTest.PORT) + port=VMwareAPISessionTest.PORT, + cacert=self.cert_mock, + insecure=False) def test_vim(self): api_session = self._create_api_session(False) @@ -126,7 +129,9 @@ class VMwareAPISessionTest(base.TestCase): self.VimMock.assert_called_with(protocol=api_session._scheme, host=VMwareAPISessionTest.SERVER_IP, port=VMwareAPISessionTest.PORT, - wsdl_url=api_session._vim_wsdl_loc) + wsdl_url=api_session._vim_wsdl_loc, + cacert=self.cert_mock, + insecure=False) @mock.patch.object(pbm, 'Pbm') def test_pbm(self, pbm_mock): diff --git a/tests/test_service.py b/tests/test_service.py index 573772a..bb5d709 100644 --- a/tests/test_service.py +++ b/tests/test_service.py @@ -14,9 +14,9 @@ # under the License. import httplib -import urllib2 import mock +import requests import suds from oslo.vmware import exceptions @@ -202,13 +202,13 @@ class ServiceTest(base.TestCase): svc_obj.powerOn, managed_object) - def test_request_handler_with_url_error(self): + def test_request_handler_with_connection_error(self): managed_object = 'VirtualMachine' def side_effect(mo, **kwargs): self.assertEqual(managed_object, mo._type) self.assertEqual(managed_object, mo.value) - raise urllib2.URLError(None) + raise requests.ConnectionError() svc_obj = service.Service() attr_name = 'powerOn' @@ -224,7 +224,7 @@ class ServiceTest(base.TestCase): def side_effect(mo, **kwargs): self.assertEqual(managed_object, mo._type) self.assertEqual(managed_object, mo.value) - raise urllib2.HTTPError(None, None, None, None, None) + raise requests.HTTPError() svc_obj = service.Service() attr_name = 'powerOn' |