oslo.vmware-2.26.0/0000775000175100017510000000000013224676314014111 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/HACKING.rst0000666000175100017510000000022213224676076015712 0ustar zuulzuul00000000000000 Style Commandments ==================== Read the OpenStack Style Commandments in the following link https://docs.openstack.org/hacking/latest/ oslo.vmware-2.26.0/test-requirements.txt0000666000175100017510000000135113224676113020351 0ustar zuulzuul00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. # Hacking already pins down pep8, pyflakes and flake8 hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD mock>=2.0.0 # BSD testrepository>=0.0.18 # Apache-2.0/BSD testtools>=2.2.0 # MIT # when we can require tox>= 1.4, this can go into tox.ini: # [testenv:cover] # deps = {[testenv]deps} coverage coverage!=4.4,>=4.0 # Apache-2.0 # this is required for the docs build jobs openstackdocstheme>=1.17.0 # Apache-2.0 sphinx>=1.6.2 # BSD reno>=2.5.0 # Apache-2.0 bandit>=1.1.0 # Apache-2.0 ddt>=1.0.1 # MIT oslo.vmware-2.26.0/CONTRIBUTING.rst0000666000175100017510000000111213224676076016554 0ustar zuulzuul00000000000000============= Contributing ============= If you would like to contribute to the development of OpenStack, you must follow the steps in this page: https://docs.openstack.org/infra/manual/developers.html Once those steps have been completed, changes to OpenStack should be submitted for review via the Gerrit tool, following the workflow documented at: https://docs.openstack.org/infra/manual/developers.html#development-workflow Pull requests submitted through GitHub will be ignored. Bugs should be filed on Launchpad, not GitHub: https://bugs.launchpad.net/oslo.vmware oslo.vmware-2.26.0/oslo_vmware/0000775000175100017510000000000013224676314016446 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/oslo_vmware/api.py0000666000175100017510000005730313224676076017610 0ustar zuulzuul00000000000000# Copyright (c) 2014 VMware, Inc. # All Rights Reserved. # # 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. """ Session and API call management for VMware ESX/VC server. This module contains classes to invoke VIM APIs. It supports automatic session re-establishment and retry of API invocations in case of connection problems or server API call overload. """ import logging from oslo_concurrency import lockutils from oslo_utils import excutils from oslo_utils import reflection import six from oslo_vmware._i18n import _ from oslo_vmware.common import loopingcall from oslo_vmware import exceptions from oslo_vmware import pbm from oslo_vmware import vim from oslo_vmware import vim_util LOG = logging.getLogger(__name__) def _trunc_id(session_id): """Returns truncated session id which is suitable for logging.""" if session_id is not None: return session_id[-5:] # TODO(vbala) Move this class to excutils.py. class RetryDecorator(object): """Decorator for retrying a function upon suggested exceptions. The decorated function is retried for the given number of times, and the sleep time between the retries is incremented until max sleep time is reached. If the max retry count is set to -1, then the decorated function is invoked indefinitely until an exception is thrown, and the caught exception is not in the list of suggested exceptions. """ def __init__(self, max_retry_count=-1, inc_sleep_time=10, max_sleep_time=60, exceptions=()): """Configure the retry object using the input params. :param max_retry_count: maximum number of times the given function must be retried when one of the input 'exceptions' is caught. When set to -1, it will be retried indefinitely until an exception is thrown and the caught exception is not in param exceptions. :param inc_sleep_time: incremental time in seconds for sleep time between retries :param max_sleep_time: max sleep time in seconds beyond which the sleep time will not be incremented using param inc_sleep_time. On reaching this threshold, max_sleep_time will be used as the sleep time. :param exceptions: suggested exceptions for which the function must be retried """ self._max_retry_count = max_retry_count self._inc_sleep_time = inc_sleep_time self._max_sleep_time = max_sleep_time self._exceptions = exceptions self._retry_count = 0 self._sleep_time = 0 def __call__(self, f): func_name = reflection.get_callable_name(f) def _func(*args, **kwargs): result = None try: if self._retry_count: LOG.debug("Invoking %(func_name)s; retry count is " "%(retry_count)d.", {'func_name': func_name, 'retry_count': self._retry_count}) result = f(*args, **kwargs) except self._exceptions: with excutils.save_and_reraise_exception() as ctxt: LOG.warning("Exception which is in the suggested list " "of exceptions occurred while invoking " "function: %s.", func_name, exc_info=True) if (self._max_retry_count != -1 and self._retry_count >= self._max_retry_count): LOG.error("Cannot retry upon suggested exception " "since retry count (%(retry_count)d) " "reached max retry count " "(%(max_retry_count)d).", {'retry_count': self._retry_count, 'max_retry_count': self._max_retry_count}) else: ctxt.reraise = False self._retry_count += 1 self._sleep_time += self._inc_sleep_time return self._sleep_time raise loopingcall.LoopingCallDone(result) def func(*args, **kwargs): loop = loopingcall.DynamicLoopingCall(_func, *args, **kwargs) evt = loop.start(periodic_interval_max=self._max_sleep_time) return evt.wait() return func class VMwareAPISession(object): """Setup a session with the server and handles all calls made to it. Example: api_session = VMwareAPISession('10.1.2.3', 'administrator', 'password', 10, 0.1, create_session=False, port=443) result = api_session.invoke_api(vim_util, 'get_objects', api_session.vim, 'HostSystem', 100) """ 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, cacert=None, insecure=True, pool_size=10, connection_timeout=None, op_id_prefix='oslo.vmware'): """Initializes the API session with given parameters. :param host: ESX/VC server IP address or host name :param port: port for connection :param server_username: username of ESX/VC server admin user :param server_password: password for param server_username :param api_retry_count: number of times an API must be retried upon session/connection related errors :param task_poll_interval: sleep time in seconds for polling an on-going async task as part of the API call :param scheme: protocol-- http or https :param create_session: whether to setup a connection at the time of 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 :param pool_size: Maximum number of connections in http connection pool :param connection_timeout: Maximum time in seconds to wait for peer to respond. :param op_id_prefix: String prefix for the operation ID. :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException """ self._host = host self._port = port self._server_username = server_username self._server_password = server_password self._api_retry_count = api_retry_count self._task_poll_interval = task_poll_interval self._scheme = scheme self._vim_wsdl_loc = wsdl_loc self._pbm_wsdl_loc = pbm_wsdl_loc self._session_id = None self._session_username = None self._vim = None self._pbm = None self._cacert = cacert self._insecure = insecure self._pool_size = pool_size self._connection_timeout = connection_timeout self._op_id_prefix = op_id_prefix if create_session: self._create_session() def pbm_wsdl_loc_set(self, pbm_wsdl_loc): self._pbm_wsdl_loc = pbm_wsdl_loc self._pbm = None LOG.info('PBM WSDL updated to %s', pbm_wsdl_loc) @property def vim(self): if not self._vim: self._vim = vim.Vim(protocol=self._scheme, host=self._host, port=self._port, wsdl_url=self._vim_wsdl_loc, cacert=self._cacert, insecure=self._insecure, pool_maxsize=self._pool_size, connection_timeout=self._connection_timeout, op_id_prefix=self._op_id_prefix) return self._vim @property def pbm(self): if not self._pbm and self._pbm_wsdl_loc: self._pbm = pbm.Pbm(protocol=self._scheme, host=self._host, port=self._port, wsdl_url=self._pbm_wsdl_loc, cacert=self._cacert, insecure=self._insecure, pool_maxsize=self._pool_size, connection_timeout=self._connection_timeout, op_id_prefix=self._op_id_prefix) if self._session_id: # To handle the case where pbm property is accessed after # session creation. If pbm property is accessed before session # creation, we set the cookie in _create_session. self._pbm.set_soap_cookie(self._vim.get_http_cookie()) return self._pbm @RetryDecorator(exceptions=(exceptions.VimConnectionException,)) @lockutils.synchronized('oslo_vmware_api_lock') def _create_session(self): """Establish session with the server.""" # Another thread might have created the session while the current one # was waiting for the lock. if self._session_id and self.is_current_session_active(): LOG.debug("Current session: %s is active.", _trunc_id(self._session_id)) return session_manager = self.vim.service_content.sessionManager # Login and create new session with the server for making API calls. LOG.debug("Logging into host: %s.", self._host) session = self.vim.Login(session_manager, userName=self._server_username, password=self._server_password) self._session_id = session.key # We need to save the username in the session since we may need it # later to check active session. The SessionIsActive method requires # the username parameter to be exactly same as that in the session # object. We can't use the username used for login since the Login # method ignores the case. self._session_username = session.userName LOG.info("Successfully established new session; session ID is " "%s.", _trunc_id(self._session_id)) # Set PBM client cookie. if self._pbm is not None: self._pbm.set_soap_cookie(self._vim.get_http_cookie()) def logout(self): """Log out and terminate the current session.""" if self._session_id: LOG.info("Logging out and terminating the current session " "with ID = %s.", _trunc_id(self._session_id)) try: self.vim.Logout(self.vim.service_content.sessionManager) self._session_id = None except Exception: LOG.exception("Error occurred while logging out and " "terminating the current session with " "ID = %s.", _trunc_id(self._session_id)) else: LOG.debug("No session exists to log out.") def invoke_api(self, module, method, *args, **kwargs): """Wrapper method for invoking APIs. The API call is retried in the event of exceptions due to session overload or connection problems. :param module: module corresponding to the VIM API call :param method: method in the module which corresponds to the VIM API call :param args: arguments to the method :param kwargs: keyword arguments to the method :returns: response from the API call :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException, VimConnectionException """ @RetryDecorator(max_retry_count=self._api_retry_count, exceptions=(exceptions.VimSessionOverLoadException, exceptions.VimConnectionException)) def _invoke_api(module, method, *args, **kwargs): try: api_method = getattr(module, method) return api_method(*args, **kwargs) except exceptions.VimFaultException as excep: # If this is due to an inactive session, we should re-create # the session and retry. if exceptions.NOT_AUTHENTICATED in excep.fault_list: # The NotAuthenticated fault is set by the fault checker # due to an empty response. An empty response could be a # valid response; for e.g., response for the query to # return the VMs in an ESX server which has no VMs in it. # Also, the server responds with an empty response in the # case of an inactive session. Therefore, we need a way to # differentiate between these two cases. if self.is_current_session_active(): LOG.debug("Returning empty response for " "%(module)s.%(method)s invocation.", {'module': module, 'method': method}) return [] else: # empty response is due to an inactive session excep_msg = ( _("Current session: %(session)s is inactive; " "re-creating the session while invoking " "method %(module)s.%(method)s.") % {'session': _trunc_id(self._session_id), 'module': module, 'method': method}) LOG.debug(excep_msg) self._create_session() raise exceptions.VimConnectionException(excep_msg, excep) else: # no need to retry for other VIM faults like # InvalidArgument # Raise specific exceptions here if possible if excep.fault_list: LOG.debug("Fault list: %s", excep.fault_list) fault = excep.fault_list[0] clazz = exceptions.get_fault_class(fault) if clazz: raise clazz(six.text_type(excep), details=excep.details) raise except exceptions.VimConnectionException: with excutils.save_and_reraise_exception(): # Re-create the session during connection exception only # if the session has expired. Otherwise, it could be # a transient issue. if not self.is_current_session_active(): LOG.debug("Re-creating session due to connection " "problems while invoking method " "%(module)s.%(method)s.", {'module': module, 'method': method}) self._create_session() return _invoke_api(module, method, *args, **kwargs) def is_current_session_active(self): """Check if current session is active. :returns: True if the session is active; False otherwise """ LOG.debug("Checking if the current session: %s is active.", _trunc_id(self._session_id)) is_active = False try: is_active = self.vim.SessionIsActive( self.vim.service_content.sessionManager, sessionID=self._session_id, userName=self._session_username) except exceptions.VimException as ex: LOG.debug("Error: %(error)s occurred while checking whether the " "current session: %(session)s is active.", {'error': six.text_type(ex), 'session': _trunc_id(self._session_id)}) return is_active def wait_for_task(self, task): """Waits for the given task to complete and returns the result. The task is polled until it is done. The method returns the task information upon successful completion. In case of any error, appropriate exception is raised. :param task: managed object reference of the task :returns: task info upon successful completion of the task :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException, VimConnectionException """ loop = loopingcall.FixedIntervalLoopingCall(self._poll_task, task) evt = loop.start(self._task_poll_interval) LOG.debug("Waiting for the task: %s to complete.", task) return evt.wait() def _poll_task(self, task): """Poll the given task until completion. If the task completes successfully, the method returns the task info using the input event (param done). In case of any error, appropriate exception is set in the event. :param task: managed object reference of the task """ try: # we poll tasks too often, so skip logging the opID as it generates # too much noise in the logs task_info = self.invoke_api(vim_util, 'get_object_property', self.vim, task, 'info', skip_op_id=True) except exceptions.VimException: with excutils.save_and_reraise_exception(): LOG.exception("Error occurred while reading info of " "task: %s.", task) else: task_detail = {'id': task.value} # some internal tasks do not have 'name' set if getattr(task_info, 'name', None): task_detail['name'] = task_info.name if task_info.state in ['queued', 'running']: if hasattr(task_info, 'progress'): LOG.debug("Task: %(task)s progress is %(progress)s%%.", {'task': task_detail, 'progress': task_info.progress}) elif task_info.state == 'success': def get_completed_task(): complete_time = getattr(task_info, 'completeTime', None) if complete_time: duration = complete_time - task_info.queueTime task_detail['duration_secs'] = duration.total_seconds() return task_detail LOG.debug("Task: %s completed successfully.", get_completed_task()) raise loopingcall.LoopingCallDone(task_info) else: error_msg = six.text_type(task_info.error.localizedMessage) error = task_info.error name = error.fault.__class__.__name__ fault_class = exceptions.get_fault_class(name) if fault_class: task_ex = fault_class(error_msg) else: task_ex = exceptions.VimFaultException([name], error_msg) raise task_ex def wait_for_lease_ready(self, lease): """Waits for the given lease to be ready. This method return when the lease is ready. In case of any error, appropriate exception is raised. :param lease: lease to be checked for :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException, VimConnectionException """ loop = loopingcall.FixedIntervalLoopingCall(self._poll_lease, lease) evt = loop.start(self._task_poll_interval) LOG.debug("Waiting for the lease: %s to be ready.", lease) evt.wait() def _poll_lease(self, lease): """Poll the state of the given lease. When the lease is ready, the event (param done) is notified. In case of any error, appropriate exception is set in the event. :param lease: lease whose state is to be polled """ try: state = self.invoke_api(vim_util, 'get_object_property', self.vim, lease, 'state', skip_op_id=True) except exceptions.VimException: with excutils.save_and_reraise_exception(): LOG.exception("Error occurred while checking " "state of lease: %s.", lease) else: if state == 'ready': LOG.debug("Lease: %s is ready.", lease) raise loopingcall.LoopingCallDone() elif state == 'initializing': LOG.debug("Lease: %s is initializing.", lease) elif state == 'error': LOG.debug("Invoking VIM API to read lease: %s error.", lease) error_msg = self._get_error_message(lease) excep_msg = _("Lease: %(lease)s is in error state. Details: " "%(error_msg)s.") % {'lease': lease, 'error_msg': error_msg} LOG.error(excep_msg) raise exceptions.VimException(excep_msg) else: # unknown state excep_msg = _("Unknown state: %(state)s for lease: " "%(lease)s.") % {'state': state, 'lease': lease} LOG.error(excep_msg) raise exceptions.VimException(excep_msg) def _get_error_message(self, lease): """Get error message associated with the given lease.""" try: return self.invoke_api(vim_util, 'get_object_property', self.vim, lease, 'error') except exceptions.VimException: LOG.warning("Error occurred while reading error message for " "lease: %s.", lease, exc_info=True) return "Unknown" oslo.vmware-2.26.0/oslo_vmware/tests/0000775000175100017510000000000013224676314017610 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/oslo_vmware/tests/test.ovf0000666000175100017510000001715213224676076021320 0ustar zuulzuul00000000000000 Virtual disk information The list of logical networks The dvportgroup-81 network A virtual machine test The kind of installed guest operating system Virtual hardware requirements Virtual Hardware Family 0 test vmx-10 hertz * 10^6 Number of Virtual CPUs 1 virtual CPU(s) 1 3 1 byte * 2^20 Memory Size 512MB of memory 2 4 512 1 IDE Controller VirtualIDEController 1 3 5 0 IDE Controller VirtualIDEController 0 4 5 false VirtualVideoCard 5 24 false VirtualVMCIDevice 6 vmware.vmci 1 1 true CD-ROM 1 ovf:/file/file1 7 4 vmware.cdrom.iso 15 0 Hard Disk 1 ovf:/disk/vmdisk1 8 4 17 7 true dvportgroup-81 E1000 ethernet adapter on "dvportgroup-81" Ethernet 1 9 E1000 10 A human-readable annotation foo oslo.vmware-2.26.0/oslo_vmware/tests/test_pbm.py0000666000175100017510000001747313224676076022022 0ustar zuulzuul00000000000000# Copyright (c) 2014 VMware, Inc. # All Rights Reserved. # # 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. """ Unit tests for PBM utility methods. """ import os import mock import six.moves.urllib.parse as urlparse import six.moves.urllib.request as urllib from oslo_vmware import pbm from oslo_vmware.tests import base class PBMUtilityTest(base.TestCase): """Tests for PBM utility methods.""" def test_get_all_profiles(self): session = mock.Mock() session.pbm = mock.Mock() profile_ids = mock.Mock() def invoke_api_side_effect(module, method, *args, **kwargs): self.assertEqual(session.pbm, module) self.assertIn(method, ['PbmQueryProfile', 'PbmRetrieveContent']) self.assertEqual(session.pbm.service_content.profileManager, args[0]) if method == 'PbmQueryProfile': self.assertEqual('STORAGE', kwargs['resourceType'].resourceType) return profile_ids self.assertEqual(profile_ids, kwargs['profileIds']) session.invoke_api.side_effect = invoke_api_side_effect pbm.get_all_profiles(session) self.assertEqual(2, session.invoke_api.call_count) def test_get_all_profiles_with_no_profiles(self): session = mock.Mock() session.pbm = mock.Mock() session.invoke_api.return_value = [] profiles = pbm.get_all_profiles(session) session.invoke_api.assert_called_once_with( session.pbm, 'PbmQueryProfile', session.pbm.service_content.profileManager, resourceType=session.pbm.client.factory.create()) self.assertEqual([], profiles) def _create_profile(self, profile_id, name): profile = mock.Mock() profile.profileId = profile_id profile.name = name return profile @mock.patch.object(pbm, 'get_all_profiles') def test_get_profile_id_by_name(self, get_all_profiles): profiles = [self._create_profile(str(i), 'profile-%d' % i) for i in range(0, 10)] get_all_profiles.return_value = profiles session = mock.Mock() exp_profile_id = '5' profile_id = pbm.get_profile_id_by_name(session, 'profile-%s' % exp_profile_id) self.assertEqual(exp_profile_id, profile_id) get_all_profiles.assert_called_once_with(session) @mock.patch.object(pbm, 'get_all_profiles') def test_get_profile_id_by_name_with_invalid_profile(self, get_all_profiles): profiles = [self._create_profile(str(i), 'profile-%d' % i) for i in range(0, 10)] get_all_profiles.return_value = profiles session = mock.Mock() profile_id = pbm.get_profile_id_by_name(session, ('profile-%s' % 11)) self.assertFalse(profile_id) get_all_profiles.assert_called_once_with(session) def test_filter_hubs_by_profile(self): pbm_client = mock.Mock() session = mock.Mock() session.pbm = pbm_client hubs = mock.Mock() profile_id = 'profile-0' pbm.filter_hubs_by_profile(session, hubs, profile_id) session.invoke_api.assert_called_once_with( pbm_client, 'PbmQueryMatchingHub', pbm_client.service_content.placementSolver, hubsToSearch=hubs, profile=profile_id) def _create_datastore(self, value): ds = mock.Mock() ds.value = value return ds def test_convert_datastores_to_hubs(self): ds_values = [] datastores = [] for i in range(0, 10): value = "ds-%d" % i ds_values.append(value) datastores.append(self._create_datastore(value)) pbm_client_factory = mock.Mock() pbm_client_factory.create.side_effect = lambda *args: mock.Mock() hubs = pbm.convert_datastores_to_hubs(pbm_client_factory, datastores) self.assertEqual(len(datastores), len(hubs)) hub_ids = [hub.hubId for hub in hubs] self.assertEqual(set(ds_values), set(hub_ids)) def test_filter_datastores_by_hubs(self): ds_values = [] datastores = [] for i in range(0, 10): value = "ds-%d" % i ds_values.append(value) datastores.append(self._create_datastore(value)) hubs = [] hub_ids = ds_values[0:int(len(ds_values) / 2)] for hub_id in hub_ids: hub = mock.Mock() hub.hubId = hub_id hubs.append(hub) filtered_ds = pbm.filter_datastores_by_hubs(hubs, datastores) self.assertEqual(len(hubs), len(filtered_ds)) filtered_ds_values = [ds.value for ds in filtered_ds] self.assertEqual(set(hub_ids), set(filtered_ds_values)) def test_get_pbm_wsdl_location(self): wsdl = pbm.get_pbm_wsdl_location(None) self.assertIsNone(wsdl) def expected_wsdl(version): driver_abs_dir = os.path.abspath(os.path.dirname(pbm.__file__)) path = os.path.join(driver_abs_dir, 'wsdl', version, 'pbmService.wsdl') return urlparse.urljoin('file:', urllib.pathname2url(path)) with mock.patch('os.path.exists') as path_exists: path_exists.return_value = True wsdl = pbm.get_pbm_wsdl_location('5') self.assertEqual(expected_wsdl('5'), wsdl) wsdl = pbm.get_pbm_wsdl_location('5.5') self.assertEqual(expected_wsdl('5.5'), wsdl) wsdl = pbm.get_pbm_wsdl_location('5.5.1') self.assertEqual(expected_wsdl('5.5'), wsdl) path_exists.return_value = False wsdl = pbm.get_pbm_wsdl_location('5.5') self.assertIsNone(wsdl) def test_get_profiles(self): pbm_service = mock.Mock() session = mock.Mock(pbm=pbm_service) object_ref = mock.Mock() pbm_service.client.factory.create.return_value = object_ref profile_id = mock.sentinel.profile_id session.invoke_api.return_value = profile_id value = 'vm-1' vm = mock.Mock(value=value) ret = pbm.get_profiles(session, vm) self.assertEqual(profile_id, ret) session.invoke_api.assert_called_once_with( pbm_service, 'PbmQueryAssociatedProfile', pbm_service.service_content.profileManager, entity=object_ref) self.assertEqual(value, object_ref.key) self.assertEqual('virtualMachine', object_ref.objectType) def test_get_profiles_by_ids(self): pbm_service = mock.Mock() session = mock.Mock(pbm=pbm_service) profiles = mock.sentinel.profiles session.invoke_api.return_value = profiles profile_ids = mock.sentinel.profile_ids ret = pbm.get_profiles_by_ids(session, profile_ids) self.assertEqual(profiles, ret) session.invoke_api.assert_called_once_with( pbm_service, 'PbmRetrieveContent', pbm_service.service_content.profileManager, profileIds=profile_ids) def test_get_profiles_by_empty_ids(self): session = mock.Mock() self.assertEqual([], pbm.get_profiles_by_ids(session, [])) oslo.vmware-2.26.0/oslo_vmware/tests/test_exceptions.py0000666000175100017510000001240113224676076023407 0ustar zuulzuul00000000000000# Copyright (c) 2015 VMware, Inc. # All Rights Reserved. # # 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. """ Unit tests for exceptions module. """ from oslo_vmware._i18n import _ from oslo_vmware import exceptions from oslo_vmware.tests import base class ExceptionsTest(base.TestCase): def test_exception_summary_exception_as_list(self): # assert that if a list is fed to the VimException object # that it will error. self.assertRaises(ValueError, exceptions.VimException, [], ValueError('foo')) def test_exception_summary_string(self): e = exceptions.VimException(_("string"), ValueError("foo")) string = str(e) self.assertEqual("string\nCause: foo", string) def test_vim_fault_exception_string(self): self.assertRaises(ValueError, exceptions.VimFaultException, "bad", ValueError("argument")) def test_vim_fault_exception(self): vfe = exceptions.VimFaultException([ValueError("example")], _("cause")) string = str(vfe) self.assertEqual("cause\nFaults: [ValueError('example',)]", string) def test_vim_fault_exception_with_cause_and_details(self): vfe = exceptions.VimFaultException([ValueError("example")], "MyMessage", "FooBar", {'foo': 'bar'}) string = str(vfe) self.assertEqual("MyMessage\n" "Cause: FooBar\n" "Faults: [ValueError('example',)]\n" "Details: {'foo': 'bar'}", string) def _create_subclass_exception(self): class VimSubClass(exceptions.VimException): pass return VimSubClass def test_register_fault_class(self): exc = self._create_subclass_exception() exceptions.register_fault_class('ValueError', exc) self.assertEqual(exc, exceptions.get_fault_class('ValueError')) def test_register_fault_class_override(self): exc = self._create_subclass_exception() exceptions.register_fault_class(exceptions.ALREADY_EXISTS, exc) self.assertEqual(exc, exceptions.get_fault_class(exceptions.ALREADY_EXISTS)) def test_register_fault_class_invalid(self): self.assertRaises(TypeError, exceptions.register_fault_class, 'ValueError', ValueError) def test_log_exception_to_string(self): self.assertEqual('Insufficient disk space.', str(exceptions.NoDiskSpaceException())) def test_get_fault_class(self): self.assertEqual(exceptions.AlreadyExistsException, exceptions.get_fault_class("AlreadyExists")) self.assertEqual(exceptions.CannotDeleteFileException, exceptions.get_fault_class("CannotDeleteFile")) self.assertEqual(exceptions.FileAlreadyExistsException, exceptions.get_fault_class("FileAlreadyExists")) self.assertEqual(exceptions.FileFaultException, exceptions.get_fault_class("FileFault")) self.assertEqual(exceptions.FileLockedException, exceptions.get_fault_class("FileLocked")) self.assertEqual(exceptions.FileNotFoundException, exceptions.get_fault_class("FileNotFound")) self.assertEqual(exceptions.InvalidPowerStateException, exceptions.get_fault_class("InvalidPowerState")) self.assertEqual(exceptions.InvalidPropertyException, exceptions.get_fault_class("InvalidProperty")) self.assertEqual(exceptions.NoPermissionException, exceptions.get_fault_class("NoPermission")) self.assertEqual(exceptions.NotAuthenticatedException, exceptions.get_fault_class("NotAuthenticated")) self.assertEqual(exceptions.TaskInProgress, exceptions.get_fault_class("TaskInProgress")) self.assertEqual(exceptions.DuplicateName, exceptions.get_fault_class("DuplicateName")) self.assertEqual(exceptions.NoDiskSpaceException, exceptions.get_fault_class("NoDiskSpace")) self.assertEqual(exceptions.ToolsUnavailableException, exceptions.get_fault_class("ToolsUnavailable")) self.assertEqual(exceptions.ManagedObjectNotFoundException, exceptions.get_fault_class("ManagedObjectNotFound")) # Test unknown fault. self.assertIsNone(exceptions.get_fault_class("NotAFile")) oslo.vmware-2.26.0/oslo_vmware/tests/test_vim.py0000666000175100017510000000774113224676076022034 0ustar zuulzuul00000000000000# Copyright (c) 2014 VMware, Inc. # All Rights Reserved. # # 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. """ Unit tests for classes to invoke VMware VI SOAP calls. """ import copy import mock from oslo_i18n import fixture as i18n_fixture import suds from oslo_vmware import exceptions from oslo_vmware.tests import base from oslo_vmware import vim class VimTest(base.TestCase): """Test class for Vim.""" def setUp(self): super(VimTest, self).setUp() patcher = mock.patch('suds.client.Client') self.addCleanup(patcher.stop) self.SudsClientMock = patcher.start() self.useFixture(i18n_fixture.ToggleLazy(True)) @mock.patch.object(vim.Vim, '__getattr__', autospec=True) def test_service_content(self, getattr_mock): getattr_ret = mock.Mock() getattr_mock.side_effect = lambda *args: getattr_ret vim_obj = vim.Vim() vim_obj.service_content getattr_mock.assert_called_once_with(vim_obj, 'RetrieveServiceContent') getattr_ret.assert_called_once_with('ServiceInstance') self.assertEqual(self.SudsClientMock.return_value, vim_obj.client) self.assertEqual(getattr_ret.return_value, vim_obj.service_content) def test_configure_non_default_host_port(self): vim_obj = vim.Vim('https', 'www.test.com', 12345) self.assertEqual('https://www.test.com:12345/sdk/vimService.wsdl', vim_obj.wsdl_url) self.assertEqual('https://www.test.com:12345/sdk', vim_obj.soap_url) def test_configure_ipv6(self): vim_obj = vim.Vim('https', '::1') self.assertEqual('https://[::1]/sdk/vimService.wsdl', vim_obj.wsdl_url) self.assertEqual('https://[::1]/sdk', vim_obj.soap_url) def test_configure_ipv6_and_non_default_host_port(self): vim_obj = vim.Vim('https', '::1', 12345) self.assertEqual('https://[::1]:12345/sdk/vimService.wsdl', vim_obj.wsdl_url) self.assertEqual('https://[::1]:12345/sdk', vim_obj.soap_url) def test_configure_with_wsdl_url_override(self): vim_obj = vim.Vim('https', 'www.example.com', wsdl_url='https://test.com/sdk/vimService.wsdl') self.assertEqual('https://test.com/sdk/vimService.wsdl', vim_obj.wsdl_url) self.assertEqual('https://www.example.com/sdk', vim_obj.soap_url) class VMwareSudsTest(base.TestCase): def setUp(self): super(VMwareSudsTest, self).setUp() def new_client_init(self, url, **kwargs): return mock.patch.object(suds.client.Client, '__init__', new=new_client_init).start() self.addCleanup(mock.patch.stopall) self.vim = self._vim_create() def _mock_getattr(self, attr_name): class fake_service_content(object): def __init__(self): self.ServiceContent = {} self.ServiceContent.fake = 'fake' self.assertEqual("RetrieveServiceContent", attr_name) return lambda obj, **kwargs: fake_service_content() def _vim_create(self): with mock.patch.object(vim.Vim, '__getattr__', self._mock_getattr): return vim.Vim() def test_exception_with_deepcopy(self): self.assertIsNotNone(self.vim) self.assertRaises(exceptions.VimAttributeException, copy.deepcopy, self.vim) oslo.vmware-2.26.0/oslo_vmware/tests/test_image_transfer.py0000666000175100017510000003611013224676076024217 0ustar zuulzuul00000000000000# Copyright (c) 2014 VMware, Inc. # All Rights Reserved. # # 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. """ Unit tests for functions and classes for image transfer. """ import mock import six from oslo_vmware import exceptions from oslo_vmware import image_transfer from oslo_vmware.tests import base class ImageTransferUtilityTest(base.TestCase): """Tests for image_transfer utility methods.""" def test_start_transfer(self): data = b'image-data-here' read_handle = six.BytesIO(data) write_handle = mock.Mock() image_transfer._start_transfer(read_handle, write_handle, None) write_handle.write.assert_called_once_with(data) @mock.patch('oslo_vmware.rw_handles.FileWriteHandle') @mock.patch('oslo_vmware.rw_handles.ImageReadHandle') @mock.patch.object(image_transfer, '_start_transfer') def test_download_flat_image( self, fake_transfer, fake_rw_handles_ImageReadHandle, fake_rw_handles_FileWriteHandle): context = mock.Mock() image_id = mock.Mock() image_service = mock.Mock() image_service.download = mock.Mock() image_service.download.return_value = 'fake_iter' fake_ImageReadHandle = 'fake_ImageReadHandle' fake_FileWriteHandle = 'fake_FileWriteHandle' cookies = [] timeout_secs = 10 image_size = 1000 host = '127.0.0.1' port = 443 dc_path = 'dc1' ds_name = 'ds1' file_path = '/fake_path' fake_rw_handles_ImageReadHandle.return_value = fake_ImageReadHandle fake_rw_handles_FileWriteHandle.return_value = fake_FileWriteHandle image_transfer.download_flat_image( context, timeout_secs, image_service, image_id, image_size=image_size, host=host, port=port, data_center_name=dc_path, datastore_name=ds_name, cookies=cookies, file_path=file_path) image_service.download.assert_called_once_with(context, image_id) fake_rw_handles_ImageReadHandle.assert_called_once_with('fake_iter') fake_rw_handles_FileWriteHandle.assert_called_once_with( host, port, dc_path, ds_name, cookies, file_path, image_size, cacerts=None) fake_transfer.assert_called_once_with( fake_ImageReadHandle, fake_FileWriteHandle, timeout_secs) @mock.patch('oslo_vmware.rw_handles.FileWriteHandle') @mock.patch.object(image_transfer, '_start_transfer') def test_download_file(self, start_transfer, file_write_handle_cls): write_handle = mock.sentinel.write_handle file_write_handle_cls.return_value = write_handle read_handle = mock.sentinel.read_handle host = mock.sentinel.host port = mock.sentinel.port dc_name = mock.sentinel.dc_name ds_name = mock.sentinel.ds_name cookies = mock.sentinel.cookies upload_file_path = mock.sentinel.upload_file_path file_size = mock.sentinel.file_size cacerts = mock.sentinel.cacerts timeout_secs = mock.sentinel.timeout_secs image_transfer.download_file( read_handle, host, port, dc_name, ds_name, cookies, upload_file_path, file_size, cacerts, timeout_secs) file_write_handle_cls.assert_called_once_with( host, port, dc_name, ds_name, cookies, upload_file_path, file_size, cacerts=cacerts) start_transfer.assert_called_once_with( read_handle, write_handle, timeout_secs) @mock.patch('oslo_vmware.rw_handles.VmdkWriteHandle') @mock.patch.object(image_transfer, '_start_transfer') def test_download_stream_optimized_data(self, fake_transfer, fake_rw_handles_VmdkWriteHandle): context = mock.Mock() session = mock.Mock() read_handle = mock.Mock() timeout_secs = 10 image_size = 1000 host = '127.0.0.1' port = 443 resource_pool = 'rp-1' vm_folder = 'folder-1' vm_import_spec = None fake_VmdkWriteHandle = mock.Mock() fake_VmdkWriteHandle.get_imported_vm = mock.Mock() fake_rw_handles_VmdkWriteHandle.return_value = fake_VmdkWriteHandle image_transfer.download_stream_optimized_data( context, timeout_secs, read_handle, session=session, host=host, port=port, resource_pool=resource_pool, vm_folder=vm_folder, vm_import_spec=vm_import_spec, image_size=image_size) fake_rw_handles_VmdkWriteHandle.assert_called_once_with( session, host, port, resource_pool, vm_folder, vm_import_spec, image_size, 'PUT') fake_transfer.assert_called_once_with(read_handle, fake_VmdkWriteHandle, timeout_secs) fake_VmdkWriteHandle.get_imported_vm.assert_called_once_with() @mock.patch('tarfile.open') @mock.patch('oslo_vmware.image_util.get_vmdk_name_from_ovf') def test_get_vmdk_handle(self, get_vmdk_name_from_ovf, tar_open): ovf_info = mock.Mock() ovf_info.name = 'test.ovf' vmdk_info = mock.Mock() vmdk_info.name = 'test.vmdk' tar = mock.Mock() tar.__iter__ = mock.Mock(return_value=iter([ovf_info, vmdk_info])) tar.__enter__ = mock.Mock(return_value=tar) tar.__exit__ = mock.Mock(return_value=None) tar_open.return_value = tar ovf_handle = mock.Mock() get_vmdk_name_from_ovf.return_value = 'test.vmdk' vmdk_handle = mock.Mock() tar.extractfile.side_effect = [ovf_handle, vmdk_handle] ova_handle = mock.sentinel.ova_handle ret = image_transfer._get_vmdk_handle(ova_handle) self.assertEqual(vmdk_handle, ret) tar_open.assert_called_once_with(mode="r|", fileobj=ova_handle) self.assertEqual([mock.call(ovf_info), mock.call(vmdk_info)], tar.extractfile.call_args_list) get_vmdk_name_from_ovf.assert_called_once_with(ovf_handle) @mock.patch('tarfile.open') def test_get_vmdk_handle_with_invalid_ova(self, tar_open): tar = mock.Mock() tar.__iter__ = mock.Mock(return_value=iter([])) tar.__enter__ = mock.Mock(return_value=tar) tar.__exit__ = mock.Mock(return_value=None) tar_open.return_value = tar ova_handle = mock.sentinel.ova_handle ret = image_transfer._get_vmdk_handle(ova_handle) self.assertIsNone(ret) tar_open.assert_called_once_with(mode="r|", fileobj=ova_handle) self.assertFalse(tar.extractfile.called) @mock.patch('oslo_vmware.rw_handles.ImageReadHandle') @mock.patch.object(image_transfer, 'download_stream_optimized_data') @mock.patch.object(image_transfer, '_get_vmdk_handle') def _test_download_stream_optimized_image( self, get_vmdk_handle, download_stream_optimized_data, image_read_handle, container=None, invalid_ova=False): image_service = mock.Mock() if container: image_service.show.return_value = {'container_format': container} read_iter = mock.sentinel.read_iter image_service.download.return_value = read_iter read_handle = mock.sentinel.read_handle image_read_handle.return_value = read_handle if container == 'ova': if invalid_ova: get_vmdk_handle.return_value = None else: vmdk_handle = mock.sentinel.vmdk_handle get_vmdk_handle.return_value = vmdk_handle imported_vm = mock.sentinel.imported_vm download_stream_optimized_data.return_value = imported_vm context = mock.sentinel.context timeout_secs = mock.sentinel.timeout_secs image_id = mock.sentinel.image_id session = mock.sentinel.session image_size = mock.sentinel.image_size host = mock.sentinel.host port = mock.sentinel.port resource_pool = mock.sentinel.port vm_folder = mock.sentinel.vm_folder vm_import_spec = mock.sentinel.vm_import_spec if container == 'ova' and invalid_ova: self.assertRaises(exceptions.ImageTransferException, image_transfer.download_stream_optimized_image, context, timeout_secs, image_service, image_id, session=session, host=host, port=port, resource_pool=resource_pool, vm_folder=vm_folder, vm_import_spec=vm_import_spec, image_size=image_size) else: ret = image_transfer.download_stream_optimized_image( context, timeout_secs, image_service, image_id, session=session, host=host, port=port, resource_pool=resource_pool, vm_folder=vm_folder, vm_import_spec=vm_import_spec, image_size=image_size) self.assertEqual(imported_vm, ret) image_service.show.assert_called_once_with(context, image_id) image_service.download.assert_called_once_with(context, image_id) image_read_handle.assert_called_once_with(read_iter) if container == 'ova': get_vmdk_handle.assert_called_once_with(read_handle) exp_read_handle = vmdk_handle else: exp_read_handle = read_handle download_stream_optimized_data.assert_called_once_with( context, timeout_secs, exp_read_handle, session=session, host=host, port=port, resource_pool=resource_pool, vm_folder=vm_folder, vm_import_spec=vm_import_spec, image_size=image_size) def test_download_stream_optimized_image(self): self._test_download_stream_optimized_image() def test_download_stream_optimized_image_ova(self): self._test_download_stream_optimized_image(container='ova') def test_download_stream_optimized_image_invalid_ova(self): self._test_download_stream_optimized_image(container='ova', invalid_ova=True) @mock.patch.object(image_transfer, '_start_transfer') @mock.patch('oslo_vmware.rw_handles.VmdkReadHandle') def test_copy_stream_optimized_disk( self, vmdk_read_handle, start_transfer): read_handle = mock.sentinel.read_handle vmdk_read_handle.return_value = read_handle context = mock.sentinel.context timeout = mock.sentinel.timeout write_handle = mock.Mock(name='/cinder/images/tmpAbcd.vmdk') session = mock.sentinel.session host = mock.sentinel.host port = mock.sentinel.port vm = mock.sentinel.vm vmdk_file_path = mock.sentinel.vmdk_file_path vmdk_size = mock.sentinel.vmdk_size image_transfer.copy_stream_optimized_disk( context, timeout, write_handle, session=session, host=host, port=port, vm=vm, vmdk_file_path=vmdk_file_path, vmdk_size=vmdk_size) vmdk_read_handle.assert_called_once_with( session, host, port, vm, vmdk_file_path, vmdk_size) start_transfer.assert_called_once_with(read_handle, write_handle, timeout) @mock.patch('oslo_vmware.rw_handles.VmdkReadHandle') @mock.patch.object(image_transfer, '_start_transfer') def test_upload_image(self, fake_transfer, fake_rw_handles_VmdkReadHandle): context = mock.sentinel.context image_id = mock.sentinel.image_id owner_id = mock.sentinel.owner_id session = mock.sentinel.session vm = mock.sentinel.vm image_service = mock.Mock() timeout_secs = 10 image_size = 1000 host = '127.0.0.1' port = 443 file_path = '/fake_path' # TODO(vbala) Remove this after we delete the keyword argument # 'is_public' from all client code. is_public = False image_name = 'fake_image' image_version = 1 fake_VmdkReadHandle = mock.Mock() fake_rw_handles_VmdkReadHandle.return_value = fake_VmdkReadHandle image_transfer.upload_image(context, timeout_secs, image_service, image_id, owner_id, session=session, host=host, port=port, vm=vm, vmdk_file_path=file_path, vmdk_size=image_size, is_public=is_public, image_name=image_name, image_version=image_version) fake_rw_handles_VmdkReadHandle.assert_called_once_with(session, host, port, vm, file_path, image_size) ver_str = six.text_type(image_version) image_metadata = {'disk_format': 'vmdk', 'name': image_name, 'properties': {'vmware_image_version': ver_str, 'vmware_disktype': 'streamOptimized', 'owner_id': owner_id}} image_service.update.assert_called_once_with(context, image_id, image_metadata, data=fake_VmdkReadHandle) oslo.vmware-2.26.0/oslo_vmware/tests/test_image_util.py0000666000175100017510000000205413224676076023350 0ustar zuulzuul00000000000000# Copyright (c) 2016 VMware, Inc. # All Rights Reserved. # # 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. """ Unit tests for image_util. """ import os from oslo_vmware import image_util from oslo_vmware.tests import base class ImageUtilTest(base.TestCase): def test_get_vmdk_name_from_ovf(self): ovf_descriptor = os.path.join(os.path.dirname(__file__), 'test.ovf') with open(ovf_descriptor) as f: vmdk_name = image_util.get_vmdk_name_from_ovf(f) self.assertEqual("test-disk1.vmdk", vmdk_name) oslo.vmware-2.26.0/oslo_vmware/tests/__init__.py0000666000175100017510000000000013224676076021716 0ustar zuulzuul00000000000000oslo.vmware-2.26.0/oslo_vmware/tests/test_hacking.py0000666000175100017510000000231013224676076022630 0ustar zuulzuul00000000000000# Copyright (c) 2017 OpenStack Foundation. # # 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. import itertools from oslo_vmware.hacking import checks from oslo_vmware.tests import base class HackingTestCase(base.TestCase): def test_no_log_translations(self): for log, hint in itertools.product(checks._all_log_levels, checks._all_hints): bad = 'LOG.%s(%s("Bad"))' % (log, hint) self.assertEqual(1, len(list(checks.no_translate_logs(bad, 'f')))) # Catch abuses when used with a variable and not a literal bad = 'LOG.%s(%s(msg))' % (log, hint) self.assertEqual(1, len(list(checks.no_translate_logs(bad, 'f')))) oslo.vmware-2.26.0/oslo_vmware/tests/base.py0000666000175100017510000000363713224676076021114 0ustar zuulzuul00000000000000# Copyright 2010-2011 OpenStack Foundation # Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # 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. import os import fixtures import testtools _TRUE_VALUES = ('true', '1', 'yes') # FIXME(dhellmann) Update this to use oslo.test library class TestCase(testtools.TestCase): """Test case base class for all unit tests.""" def setUp(self): """Run before each test method to initialize test environment.""" super(TestCase, self).setUp() test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0) try: test_timeout = int(test_timeout) except ValueError: # If timeout value is invalid do not set a timeout. test_timeout = 0 if test_timeout > 0: self.useFixture(fixtures.Timeout(test_timeout, gentle=True)) self.useFixture(fixtures.NestedTempfile()) self.useFixture(fixtures.TempHomeDir()) if os.environ.get('OS_STDOUT_CAPTURE') in _TRUE_VALUES: stdout = self.useFixture(fixtures.StringStream('stdout')).stream self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout)) if os.environ.get('OS_STDERR_CAPTURE') in _TRUE_VALUES: stderr = self.useFixture(fixtures.StringStream('stderr')).stream self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr)) self.log_fixture = self.useFixture(fixtures.FakeLogger()) oslo.vmware-2.26.0/oslo_vmware/tests/test_rw_handles.py0000666000175100017510000003424213224676076023363 0ustar zuulzuul00000000000000# Copyright (c) 2014 VMware, Inc. # All Rights Reserved. # # 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. """ Unit tests for read and write handles for image transfer. """ import ssl import mock import six from oslo_vmware import exceptions from oslo_vmware import rw_handles from oslo_vmware.tests import base from oslo_vmware import vim_util class FileHandleTest(base.TestCase): """Tests for FileHandle.""" def test_close(self): file_handle = mock.Mock() vmw_http_file = rw_handles.FileHandle(file_handle) vmw_http_file.close() file_handle.close.assert_called_once_with() @mock.patch('urllib3.connection.HTTPConnection') def test_create_connection_http(self, http_conn): conn = mock.Mock() http_conn.return_value = conn handle = rw_handles.FileHandle(None) ret = handle._create_connection('http://localhost/foo?q=bar', 'GET') self.assertEqual(conn, ret) conn.putrequest.assert_called_once_with('GET', '/foo?q=bar') @mock.patch('urllib3.connection.HTTPSConnection') def test_create_connection_https(self, https_conn): conn = mock.Mock() https_conn.return_value = conn handle = rw_handles.FileHandle(None) ret = handle._create_connection('https://localhost/foo?q=bar', 'GET') self.assertEqual(conn, ret) conn.set_cert.assert_called_once_with( ca_certs=None, cert_reqs=ssl.CERT_NONE, assert_fingerprint=None) conn.putrequest.assert_called_once_with('GET', '/foo?q=bar') @mock.patch('urllib3.connection.HTTPSConnection') def test_create_connection_https_with_cacerts(self, https_conn): conn = mock.Mock() https_conn.return_value = conn handle = rw_handles.FileHandle(None) ret = handle._create_connection('https://localhost/foo?q=bar', 'GET', cacerts=True) self.assertEqual(conn, ret) conn.set_cert.assert_called_once_with( ca_certs=None, cert_reqs=ssl.CERT_REQUIRED, assert_fingerprint=None) @mock.patch('urllib3.connection.HTTPSConnection') def test_create_connection_https_with_ssl_thumbprint(self, https_conn): conn = mock.Mock() https_conn.return_value = conn handle = rw_handles.FileHandle(None) cacerts = mock.sentinel.cacerts thumbprint = mock.sentinel.thumbprint ret = handle._create_connection('https://localhost/foo?q=bar', 'GET', cacerts=cacerts, ssl_thumbprint=thumbprint) self.assertEqual(conn, ret) conn.set_cert.assert_called_once_with( ca_certs=cacerts, cert_reqs=None, assert_fingerprint=thumbprint) class FileWriteHandleTest(base.TestCase): """Tests for FileWriteHandle.""" def setUp(self): super(FileWriteHandleTest, self).setUp() vim_cookie = mock.Mock() vim_cookie.name = 'name' vim_cookie.value = 'value' self._conn = mock.Mock() patcher = mock.patch( 'urllib3.connection.HTTPConnection') self.addCleanup(patcher.stop) HTTPConnectionMock = patcher.start() HTTPConnectionMock.return_value = self._conn self.vmw_http_write_file = rw_handles.FileWriteHandle( '10.1.2.3', 443, 'dc-0', 'ds-0', [vim_cookie], '1.vmdk', 100, 'http') def test_write(self): self.vmw_http_write_file.write(None) self._conn.send.assert_called_once_with(None) def test_close(self): self.vmw_http_write_file.close() self._conn.getresponse.assert_called_once_with() self._conn.close.assert_called_once_with() class VmdkHandleTest(base.TestCase): """Tests for VmdkHandle.""" def test_find_vmdk_url(self): device_url_0 = mock.Mock() device_url_0.disk = False device_url_1 = mock.Mock() device_url_1.disk = True device_url_1.url = 'https://*/ds1/vm1.vmdk' device_url_1.sslThumbprint = '11:22:33:44:55' lease_info = mock.Mock() lease_info.deviceUrl = [device_url_0, device_url_1] host = '10.1.2.3' port = 443 exp_url = 'https://%s:%d/ds1/vm1.vmdk' % (host, port) vmw_http_file = rw_handles.VmdkHandle(None, None, None, None) url, thumbprint = vmw_http_file._find_vmdk_url(lease_info, host, port) self.assertEqual(exp_url, url) self.assertEqual('11:22:33:44:55', thumbprint) def test_update_progress(self): session = mock.Mock() lease = mock.Mock() handle = rw_handles.VmdkHandle(session, lease, 'fake-url', None) handle._get_progress = mock.Mock(return_value=50) handle.update_progress() session.invoke_api.assert_called_once_with(session.vim, 'HttpNfcLeaseProgress', lease, percent=50) def test_update_progress_with_error(self): session = mock.Mock() handle = rw_handles.VmdkHandle(session, None, 'fake-url', None) handle._get_progress = mock.Mock(return_value=0) session.invoke_api.side_effect = exceptions.VimException(None) self.assertRaises(exceptions.VimException, handle.update_progress) class VmdkWriteHandleTest(base.TestCase): """Tests for VmdkWriteHandle.""" def setUp(self): super(VmdkWriteHandleTest, self).setUp() self._conn = mock.Mock() patcher = mock.patch( 'urllib3.connection.HTTPConnection') self.addCleanup(patcher.stop) HTTPConnectionMock = patcher.start() HTTPConnectionMock.return_value = self._conn def _create_mock_session(self, disk=True, progress=-1): device_url = mock.Mock() device_url.disk = disk device_url.url = 'http://*/ds/disk1.vmdk' lease_info = mock.Mock() lease_info.deviceUrl = [device_url] session = mock.Mock() def session_invoke_api_side_effect(module, method, *args, **kwargs): if module == session.vim: if method == 'ImportVApp': return mock.Mock() elif method == 'HttpNfcLeaseProgress': self.assertEqual(progress, kwargs['percent']) return return lease_info session.invoke_api.side_effect = session_invoke_api_side_effect vim_cookie = mock.Mock() vim_cookie.name = 'name' vim_cookie.value = 'value' session.vim.client.options.transport.cookiejar = [vim_cookie] return session def test_init_failure(self): session = self._create_mock_session(False) self.assertRaises(exceptions.VimException, rw_handles.VmdkWriteHandle, session, '10.1.2.3', 443, 'rp-1', 'folder-1', None, 100) def test_write(self): session = self._create_mock_session() handle = rw_handles.VmdkWriteHandle(session, '10.1.2.3', 443, 'rp-1', 'folder-1', None, 100) data = [1] * 10 handle.write(data) self.assertEqual(len(data), handle._bytes_written) self._conn.putrequest.assert_called_once_with('PUT', '/ds/disk1.vmdk') self._conn.send.assert_called_once_with(data) def test_write_post(self): session = self._create_mock_session() handle = rw_handles.VmdkWriteHandle(session, '10.1.2.3', 443, 'rp-1', 'folder-1', None, 100, http_method='POST') data = [1] * 10 handle.write(data) self.assertEqual(len(data), handle._bytes_written) self._conn.putrequest.assert_called_once_with('POST', '/ds/disk1.vmdk') self._conn.send.assert_called_once_with(data) def test_update_progress(self): vmdk_size = 100 data_size = 10 session = self._create_mock_session(True, 10) handle = rw_handles.VmdkWriteHandle(session, '10.1.2.3', 443, 'rp-1', 'folder-1', None, vmdk_size) handle.write([1] * data_size) handle.update_progress() def test_close(self): session = self._create_mock_session() handle = rw_handles.VmdkWriteHandle(session, '10.1.2.3', 443, 'rp-1', 'folder-1', None, 100) def session_invoke_api_side_effect(module, method, *args, **kwargs): if module == vim_util and method == 'get_object_property': return 'ready' self.assertEqual(session.vim, module) self.assertEqual('HttpNfcLeaseComplete', method) session.invoke_api = mock.Mock( side_effect=session_invoke_api_side_effect) handle.close() self.assertEqual(2, session.invoke_api.call_count) class VmdkReadHandleTest(base.TestCase): """Tests for VmdkReadHandle.""" def setUp(self): super(VmdkReadHandleTest, self).setUp() self._resp = mock.Mock() self._resp.read.return_value = 'fake-data' self._conn = mock.Mock() self._conn.getresponse.return_value = self._resp patcher = mock.patch( 'urllib3.connection.HTTPConnection') self.addCleanup(patcher.stop) HTTPConnectionMock = patcher.start() HTTPConnectionMock.return_value = self._conn def _create_mock_session(self, disk=True, progress=-1): device_url = mock.Mock() device_url.disk = disk device_url.url = 'http://*/ds/disk1.vmdk' lease_info = mock.Mock() lease_info.deviceUrl = [device_url] session = mock.Mock() def session_invoke_api_side_effect(module, method, *args, **kwargs): if module == session.vim: if method == 'ExportVm': return mock.Mock() elif method == 'HttpNfcLeaseProgress': self.assertEqual(progress, kwargs['percent']) return return lease_info session.invoke_api.side_effect = session_invoke_api_side_effect vim_cookie = mock.Mock() vim_cookie.name = 'name' vim_cookie.value = 'value' session.vim.client.options.transport.cookiejar = [vim_cookie] return session def test_init_failure(self): session = self._create_mock_session(False) self.assertRaises(exceptions.VimException, rw_handles.VmdkReadHandle, session, '10.1.2.3', 443, 'vm-1', '[ds] disk1.vmdk', 100) def test_read(self): chunk_size = rw_handles.READ_CHUNKSIZE session = self._create_mock_session() handle = rw_handles.VmdkReadHandle(session, '10.1.2.3', 443, 'vm-1', '[ds] disk1.vmdk', chunk_size * 10) data = handle.read(chunk_size) self.assertEqual('fake-data', data) def test_update_progress(self): chunk_size = len('fake-data') vmdk_size = chunk_size * 10 session = self._create_mock_session(True, 10) handle = rw_handles.VmdkReadHandle(session, '10.1.2.3', 443, 'vm-1', '[ds] disk1.vmdk', vmdk_size) data = handle.read(chunk_size) handle.update_progress() self.assertEqual('fake-data', data) def test_close(self): session = self._create_mock_session() handle = rw_handles.VmdkReadHandle(session, '10.1.2.3', 443, 'vm-1', '[ds] disk1.vmdk', 100) def session_invoke_api_side_effect(module, method, *args, **kwargs): if module == vim_util and method == 'get_object_property': return 'ready' self.assertEqual(session.vim, module) self.assertEqual('HttpNfcLeaseComplete', method) session.invoke_api = mock.Mock( side_effect=session_invoke_api_side_effect) handle.close() self.assertEqual(2, session.invoke_api.call_count) def test_close_with_error(self): session = self._create_mock_session() handle = rw_handles.VmdkReadHandle(session, '10.1.2.3', 443, 'vm-1', '[ds] disk1.vmdk', 100) session.invoke_api.side_effect = exceptions.VimException(None) self.assertRaises(exceptions.VimException, handle.close) self._resp.close.assert_called_once_with() class ImageReadHandleTest(base.TestCase): """Tests for ImageReadHandle.""" def test_read(self): max_items = 10 item = [1] * 10 class ImageReadIterator(six.Iterator): def __init__(self): self.num_items = 0 def __iter__(self): return self def __next__(self): if (self.num_items < max_items): self.num_items += 1 return item raise StopIteration next = __next__ handle = rw_handles.ImageReadHandle(ImageReadIterator()) for _ in range(0, max_items): self.assertEqual(item, handle.read(10)) self.assertFalse(handle.read(10)) oslo.vmware-2.26.0/oslo_vmware/tests/test_api.py0000666000175100017510000005605713224676076022016 0ustar zuulzuul00000000000000# coding=utf-8 # Copyright (c) 2014 VMware, Inc. # All Rights Reserved. # # 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. """ Unit tests for session management and API invocation classes. """ from datetime import datetime from eventlet import greenthread import mock import six import suds from oslo_vmware import api from oslo_vmware import exceptions from oslo_vmware import pbm from oslo_vmware.tests import base from oslo_vmware import vim_util class RetryDecoratorTest(base.TestCase): """Tests for retry decorator class.""" def test_retry(self): result = "RESULT" @api.RetryDecorator() def func(*args, **kwargs): return result self.assertEqual(result, func()) def func2(*args, **kwargs): return result retry = api.RetryDecorator() self.assertEqual(result, retry(func2)()) self.assertTrue(retry._retry_count == 0) def test_retry_with_expected_exceptions(self): result = "RESULT" responses = [exceptions.VimSessionOverLoadException(None), exceptions.VimSessionOverLoadException(None), result] def func(*args, **kwargs): response = responses.pop(0) if isinstance(response, Exception): raise response return response sleep_time_incr = 0.01 retry_count = 2 retry = api.RetryDecorator(10, sleep_time_incr, 10, (exceptions.VimSessionOverLoadException,)) self.assertEqual(result, retry(func)()) self.assertTrue(retry._retry_count == retry_count) self.assertEqual(retry_count * sleep_time_incr, retry._sleep_time) def test_retry_with_max_retries(self): responses = [exceptions.VimSessionOverLoadException(None), exceptions.VimSessionOverLoadException(None), exceptions.VimSessionOverLoadException(None)] def func(*args, **kwargs): response = responses.pop(0) if isinstance(response, Exception): raise response return response retry = api.RetryDecorator(2, 0, 0, (exceptions.VimSessionOverLoadException,)) self.assertRaises(exceptions.VimSessionOverLoadException, retry(func)) self.assertTrue(retry._retry_count == 2) def test_retry_with_unexpected_exception(self): def func(*args, **kwargs): raise exceptions.VimException(None) retry = api.RetryDecorator() self.assertRaises(exceptions.VimException, retry(func)) self.assertTrue(retry._retry_count == 0) class VMwareAPISessionTest(base.TestCase): """Tests for VMwareAPISession.""" SERVER_IP = '10.1.2.3' PORT = 443 USERNAME = 'admin' PASSWORD = 'password' POOL_SIZE = 15 def setUp(self): super(VMwareAPISessionTest, self).setUp() patcher = mock.patch('oslo_vmware.vim.Vim') 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): return api.VMwareAPISession(VMwareAPISessionTest.SERVER_IP, VMwareAPISessionTest.USERNAME, VMwareAPISessionTest.PASSWORD, retry_count, task_poll_interval, 'https', _create_session, port=VMwareAPISessionTest.PORT, cacert=self.cert_mock, insecure=False, pool_size=VMwareAPISessionTest.POOL_SIZE) def test_vim(self): api_session = self._create_api_session(False) api_session.vim self.VimMock.assert_called_with( protocol=api_session._scheme, host=VMwareAPISessionTest.SERVER_IP, port=VMwareAPISessionTest.PORT, wsdl_url=api_session._vim_wsdl_loc, cacert=self.cert_mock, insecure=False, pool_maxsize=VMwareAPISessionTest.POOL_SIZE, connection_timeout=None, op_id_prefix='oslo.vmware') @mock.patch.object(pbm, 'Pbm') def test_pbm(self, pbm_mock): api_session = self._create_api_session(True) vim_obj = api_session.vim cookie = mock.Mock() vim_obj.get_http_cookie.return_value = cookie api_session._pbm_wsdl_loc = mock.Mock() pbm = mock.Mock() pbm_mock.return_value = pbm api_session._get_session_cookie = mock.Mock(return_value=cookie) self.assertEqual(pbm, api_session.pbm) pbm.set_soap_cookie.assert_called_once_with(cookie) def test_create_session(self): session = mock.Mock() session.key = "12345" api_session = self._create_api_session(False) cookie = mock.Mock() vim_obj = api_session.vim vim_obj.Login.return_value = session vim_obj.get_http_cookie.return_value = cookie pbm = mock.Mock() api_session._pbm = pbm api_session._create_session() session_manager = vim_obj.service_content.sessionManager vim_obj.Login.assert_called_once_with( session_manager, userName=VMwareAPISessionTest.USERNAME, password=VMwareAPISessionTest.PASSWORD) self.assertFalse(vim_obj.TerminateSession.called) self.assertEqual(session.key, api_session._session_id) pbm.set_soap_cookie.assert_called_once_with(cookie) def test_create_session_with_existing_inactive_session(self): old_session_key = '12345' new_session_key = '67890' session = mock.Mock() session.key = new_session_key api_session = self._create_api_session(False) api_session._session_id = old_session_key api_session._session_username = api_session._server_username vim_obj = api_session.vim vim_obj.Login.return_value = session vim_obj.SessionIsActive.return_value = False api_session._create_session() session_manager = vim_obj.service_content.sessionManager vim_obj.SessionIsActive.assert_called_once_with( session_manager, sessionID=old_session_key, userName=VMwareAPISessionTest.USERNAME) vim_obj.Login.assert_called_once_with( session_manager, userName=VMwareAPISessionTest.USERNAME, password=VMwareAPISessionTest.PASSWORD) self.assertEqual(new_session_key, api_session._session_id) def test_create_session_with_existing_active_session(self): old_session_key = '12345' api_session = self._create_api_session(False) api_session._session_id = old_session_key api_session._session_username = api_session._server_username vim_obj = api_session.vim vim_obj.SessionIsActive.return_value = True api_session._create_session() session_manager = vim_obj.service_content.sessionManager vim_obj.SessionIsActive.assert_called_once_with( session_manager, sessionID=old_session_key, userName=VMwareAPISessionTest.USERNAME) self.assertFalse(vim_obj.Login.called) self.assertEqual(old_session_key, api_session._session_id) def test_invoke_api(self): api_session = self._create_api_session(True) response = mock.Mock() def api(*args, **kwargs): return response module = mock.Mock() module.api = api ret = api_session.invoke_api(module, 'api') self.assertEqual(response, ret) def test_logout_with_exception(self): session = mock.Mock() session.key = "12345" api_session = self._create_api_session(False) vim_obj = api_session.vim vim_obj.Login.return_value = session vim_obj.Logout.side_effect = exceptions.VimFaultException([], None) api_session._create_session() api_session.logout() self.assertEqual("12345", api_session._session_id) def test_logout_no_session(self): api_session = self._create_api_session(False) vim_obj = api_session.vim api_session.logout() self.assertEqual(0, vim_obj.Logout.call_count) def test_logout_calls_vim_logout(self): session = mock.Mock() session.key = "12345" api_session = self._create_api_session(False) vim_obj = api_session.vim vim_obj.Login.return_value = session vim_obj.Logout.return_value = None api_session._create_session() session_manager = vim_obj.service_content.sessionManager vim_obj.Login.assert_called_once_with( session_manager, userName=VMwareAPISessionTest.USERNAME, password=VMwareAPISessionTest.PASSWORD) api_session.logout() vim_obj.Logout.assert_called_once_with( session_manager) self.assertIsNone(api_session._session_id) def test_invoke_api_with_expected_exception(self): api_session = self._create_api_session(True) api_session._create_session = mock.Mock() vim_obj = api_session.vim vim_obj.SessionIsActive.return_value = False ret = mock.Mock() responses = [exceptions.VimConnectionException(None), ret] def api(*args, **kwargs): response = responses.pop(0) if isinstance(response, Exception): raise response return response module = mock.Mock() module.api = api with mock.patch.object(greenthread, 'sleep'): self.assertEqual(ret, api_session.invoke_api(module, 'api')) api_session._create_session.assert_called_once_with() def test_invoke_api_not_recreate_session(self): api_session = self._create_api_session(True) api_session._create_session = mock.Mock() vim_obj = api_session.vim vim_obj.SessionIsActive.return_value = True ret = mock.Mock() responses = [exceptions.VimConnectionException(None), ret] def api(*args, **kwargs): response = responses.pop(0) if isinstance(response, Exception): raise response return response module = mock.Mock() module.api = api with mock.patch.object(greenthread, 'sleep'): self.assertEqual(ret, api_session.invoke_api(module, 'api')) self.assertFalse(api_session._create_session.called) def test_invoke_api_with_vim_fault_exception(self): api_session = self._create_api_session(True) def api(*args, **kwargs): raise exceptions.VimFaultException([], None) module = mock.Mock() module.api = api self.assertRaises(exceptions.VimFaultException, api_session.invoke_api, module, 'api') def test_invoke_api_with_vim_fault_exception_details(self): api_session = self._create_api_session(True) fault_string = 'Invalid property.' fault_list = [exceptions.INVALID_PROPERTY] details = {u'name': suds.sax.text.Text(u'фира')} module = mock.Mock() module.api.side_effect = exceptions.VimFaultException(fault_list, fault_string, details=details) e = self.assertRaises(exceptions.InvalidPropertyException, api_session.invoke_api, module, 'api') details_str = u"{'name': 'фира'}" expected_str = "%s\nFaults: %s\nDetails: %s" % (fault_string, fault_list, details_str) self.assertEqual(expected_str, six.text_type(e)) self.assertEqual(details, e.details) def test_invoke_api_with_empty_response(self): api_session = self._create_api_session(True) vim_obj = api_session.vim vim_obj.SessionIsActive.return_value = True def api(*args, **kwargs): raise exceptions.VimFaultException( [exceptions.NOT_AUTHENTICATED], None) module = mock.Mock() module.api = api ret = api_session.invoke_api(module, 'api') self.assertEqual([], ret) vim_obj.SessionIsActive.assert_called_once_with( vim_obj.service_content.sessionManager, sessionID=api_session._session_id, userName=api_session._session_username) def test_invoke_api_with_stale_session(self): api_session = self._create_api_session(True) api_session._create_session = mock.Mock() vim_obj = api_session.vim vim_obj.SessionIsActive.return_value = False result = mock.Mock() responses = [exceptions.VimFaultException( [exceptions.NOT_AUTHENTICATED], None), result] def api(*args, **kwargs): response = responses.pop(0) if isinstance(response, Exception): raise response return response module = mock.Mock() module.api = api with mock.patch.object(greenthread, 'sleep'): ret = api_session.invoke_api(module, 'api') self.assertEqual(result, ret) vim_obj.SessionIsActive.assert_called_once_with( vim_obj.service_content.sessionManager, sessionID=api_session._session_id, userName=api_session._session_username) api_session._create_session.assert_called_once_with() def test_invoke_api_with_unknown_fault(self): api_session = self._create_api_session(True) fault_list = ['NotAFile'] module = mock.Mock() module.api.side_effect = exceptions.VimFaultException(fault_list, 'Not a file.') ex = self.assertRaises(exceptions.VimFaultException, api_session.invoke_api, module, 'api') self.assertEqual(fault_list, ex.fault_list) def test_wait_for_task(self): api_session = self._create_api_session(True) task_info_list = [('queued', 0), ('running', 40), ('success', 100)] task_info_list_size = len(task_info_list) def invoke_api_side_effect(module, method, *args, **kwargs): (state, progress) = task_info_list.pop(0) task_info = mock.Mock() task_info.progress = progress task_info.queueTime = datetime(2016, 12, 6, 15, 29, 43, 79060) task_info.completeTime = datetime(2016, 12, 6, 15, 29, 50, 79060) task_info.state = state return task_info api_session.invoke_api = mock.Mock(side_effect=invoke_api_side_effect) task = mock.Mock() with mock.patch.object(greenthread, 'sleep'): ret = api_session.wait_for_task(task) self.assertEqual('success', ret.state) self.assertEqual(100, ret.progress) api_session.invoke_api.assert_called_with(vim_util, 'get_object_property', api_session.vim, task, 'info', skip_op_id=True) self.assertEqual(task_info_list_size, api_session.invoke_api.call_count) def test_wait_for_task_with_error_state(self): api_session = self._create_api_session(True) task_info_list = [('queued', 0), ('running', 40), ('error', -1)] task_info_list_size = len(task_info_list) def invoke_api_side_effect(module, method, *args, **kwargs): (state, progress) = task_info_list.pop(0) task_info = mock.Mock() task_info.progress = progress task_info.state = state return task_info api_session.invoke_api = mock.Mock(side_effect=invoke_api_side_effect) task = mock.Mock() with mock.patch.object(greenthread, 'sleep'): self.assertRaises(exceptions.VimFaultException, api_session.wait_for_task, task) api_session.invoke_api.assert_called_with(vim_util, 'get_object_property', api_session.vim, task, 'info', skip_op_id=True) self.assertEqual(task_info_list_size, api_session.invoke_api.call_count) def test_wait_for_task_with_invoke_api_exception(self): api_session = self._create_api_session(True) api_session.invoke_api = mock.Mock( side_effect=exceptions.VimException(None)) task = mock.Mock() with mock.patch.object(greenthread, 'sleep'): self.assertRaises(exceptions.VimException, api_session.wait_for_task, task) api_session.invoke_api.assert_called_once_with(vim_util, 'get_object_property', api_session.vim, task, 'info', skip_op_id=True) def test_wait_for_lease_ready(self): api_session = self._create_api_session(True) lease_states = ['initializing', 'ready'] num_states = len(lease_states) def invoke_api_side_effect(module, method, *args, **kwargs): return lease_states.pop(0) api_session.invoke_api = mock.Mock(side_effect=invoke_api_side_effect) lease = mock.Mock() with mock.patch.object(greenthread, 'sleep'): api_session.wait_for_lease_ready(lease) api_session.invoke_api.assert_called_with(vim_util, 'get_object_property', api_session.vim, lease, 'state', skip_op_id=True) self.assertEqual(num_states, api_session.invoke_api.call_count) def test_wait_for_lease_ready_with_error_state(self): api_session = self._create_api_session(True) responses = ['initializing', 'error', 'error_msg'] def invoke_api_side_effect(module, method, *args, **kwargs): return responses.pop(0) api_session.invoke_api = mock.Mock(side_effect=invoke_api_side_effect) lease = mock.Mock() with mock.patch.object(greenthread, 'sleep'): self.assertRaises(exceptions.VimException, api_session.wait_for_lease_ready, lease) exp_calls = [mock.call(vim_util, 'get_object_property', api_session.vim, lease, 'state', skip_op_id=True)] * 2 exp_calls.append(mock.call(vim_util, 'get_object_property', api_session.vim, lease, 'error')) self.assertEqual(exp_calls, api_session.invoke_api.call_args_list) def test_wait_for_lease_ready_with_unknown_state(self): api_session = self._create_api_session(True) def invoke_api_side_effect(module, method, *args, **kwargs): return 'unknown' api_session.invoke_api = mock.Mock(side_effect=invoke_api_side_effect) lease = mock.Mock() self.assertRaises(exceptions.VimException, api_session.wait_for_lease_ready, lease) api_session.invoke_api.assert_called_once_with(vim_util, 'get_object_property', api_session.vim, lease, 'state', skip_op_id=True) def test_wait_for_lease_ready_with_invoke_api_exception(self): api_session = self._create_api_session(True) api_session.invoke_api = mock.Mock( side_effect=exceptions.VimException(None)) lease = mock.Mock() self.assertRaises(exceptions.VimException, api_session.wait_for_lease_ready, lease) api_session.invoke_api.assert_called_once_with( vim_util, 'get_object_property', api_session.vim, lease, 'state', skip_op_id=True) def _poll_task_well_known_exceptions(self, fault, expected_exception): api_session = self._create_api_session(False) def fake_invoke_api(self, module, method, *args, **kwargs): task_info = mock.Mock() task_info.progress = -1 task_info.state = 'error' error = mock.Mock() error.localizedMessage = "Error message" error_fault = mock.Mock() error_fault.__class__.__name__ = fault error.fault = error_fault task_info.error = error return task_info with ( mock.patch.object(api_session, 'invoke_api', fake_invoke_api) ): fake_task = mock.Mock() fake_task.value = 'task-1' self.assertRaises(expected_exception, api_session._poll_task, fake_task) def test_poll_task_well_known_exceptions(self): for k, v in six.iteritems(exceptions._fault_classes_registry): self._poll_task_well_known_exceptions(k, v) def test_poll_task_unknown_exception(self): _unknown_exceptions = { 'NotAFile': exceptions.VimFaultException, 'RuntimeFault': exceptions.VimFaultException } for k, v in six.iteritems(_unknown_exceptions): self._poll_task_well_known_exceptions(k, v) def test_update_pbm_wsdl_loc(self): session = mock.Mock() session.key = "12345" api_session = self._create_api_session(False) self.assertIsNone(api_session._pbm_wsdl_loc) api_session.pbm_wsdl_loc_set('fake_wsdl') self.assertEqual('fake_wsdl', api_session._pbm_wsdl_loc) oslo.vmware-2.26.0/oslo_vmware/tests/test_service.py0000666000175100017510000005535413224676076022704 0ustar zuulzuul00000000000000# Copyright (c) 2014 VMware, Inc. # All Rights Reserved. # # 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. import mock import requests import six import six.moves.http_client as httplib import suds import ddt from oslo_vmware import exceptions from oslo_vmware import service from oslo_vmware.tests import base from oslo_vmware import vim_util @ddt.ddt class ServiceMessagePluginTest(base.TestCase): """Test class for ServiceMessagePlugin.""" def setUp(self): super(ServiceMessagePluginTest, self).setUp() self.plugin = service.ServiceMessagePlugin() @ddt.data(('value', 'foo', 'string'), ('removeKey', '1', 'int'), ('removeKey', 'foo', 'string')) @ddt.unpack def test_add_attribute_for_value(self, name, text, expected_xsd_type): node = mock.Mock() node.name = name node.text = text self.plugin.add_attribute_for_value(node) node.set.assert_called_once_with('xsi:type', 'xsd:%s' % expected_xsd_type) def test_marshalled(self): context = mock.Mock() self.plugin.prune = mock.Mock() self.plugin.marshalled(context) self.plugin.prune.assert_called_once_with(context.envelope) context.envelope.walk.assert_called_once_with( self.plugin.add_attribute_for_value) class ServiceTest(base.TestCase): def setUp(self): super(ServiceTest, self).setUp() patcher = mock.patch('suds.client.Client') self.addCleanup(patcher.stop) self.SudsClientMock = patcher.start() def test_retrieve_properties_ex_fault_checker_with_empty_response(self): ex = self.assertRaises( exceptions.VimFaultException, service.Service._retrieve_properties_ex_fault_checker, None) self.assertEqual([exceptions.NOT_AUTHENTICATED], ex.fault_list) def test_retrieve_properties_ex_fault_checker(self): fault_list = ['FileFault', 'VimFault'] missing_set = [] for fault in fault_list: missing_elem = mock.Mock() missing_elem.fault.fault.__class__.__name__ = fault missing_set.append(missing_elem) obj_cont = mock.Mock() obj_cont.missingSet = missing_set response = mock.Mock() response.objects = [obj_cont] ex = self.assertRaises( exceptions.VimFaultException, service.Service._retrieve_properties_ex_fault_checker, response) self.assertEqual(fault_list, ex.fault_list) def test_request_handler(self): managed_object = 'VirtualMachine' resp = mock.Mock() def side_effect(mo, **kwargs): self.assertEqual(managed_object, mo._type) self.assertEqual(managed_object, mo.value) return resp svc_obj = service.Service() attr_name = 'powerOn' service_mock = svc_obj.client.service setattr(service_mock, attr_name, side_effect) ret = svc_obj.powerOn(managed_object) self.assertEqual(resp, ret) def test_request_handler_with_retrieve_properties_ex_fault(self): managed_object = 'Datacenter' def side_effect(mo, **kwargs): self.assertEqual(managed_object, mo._type) self.assertEqual(managed_object, mo.value) return None svc_obj = service.Service() attr_name = 'retrievePropertiesEx' service_mock = svc_obj.client.service setattr(service_mock, attr_name, side_effect) self.assertRaises(exceptions.VimFaultException, svc_obj.retrievePropertiesEx, managed_object) def test_request_handler_with_web_fault(self): managed_object = 'VirtualMachine' fault_list = ['Fault'] doc = mock.Mock() def side_effect(mo, **kwargs): self.assertEqual(managed_object, mo._type) self.assertEqual(managed_object, mo.value) fault = mock.Mock(faultstring="MyFault") fault_children = mock.Mock() fault_children.name = "name" fault_children.getText.return_value = "value" child = mock.Mock() child.get.return_value = fault_list[0] child.getChildren.return_value = [fault_children] detail = mock.Mock() detail.getChildren.return_value = [child] doc.childAtPath.return_value = detail raise suds.WebFault(fault, doc) svc_obj = service.Service() service_mock = svc_obj.client.service setattr(service_mock, 'powerOn', side_effect) ex = self.assertRaises(exceptions.VimFaultException, svc_obj.powerOn, managed_object) self.assertEqual(fault_list, ex.fault_list) self.assertEqual({'name': 'value'}, ex.details) self.assertEqual("MyFault", ex.msg) doc.childAtPath.assert_called_once_with('/detail') def test_request_handler_with_empty_web_fault_doc(self): def side_effect(mo, **kwargs): fault = mock.Mock(faultstring="MyFault") raise suds.WebFault(fault, None) svc_obj = service.Service() service_mock = svc_obj.client.service setattr(service_mock, 'powerOn', side_effect) ex = self.assertRaises(exceptions.VimFaultException, svc_obj.powerOn, 'VirtualMachine') self.assertEqual([], ex.fault_list) self.assertEqual({}, ex.details) self.assertEqual("MyFault", ex.msg) def test_request_handler_with_vc51_web_fault(self): managed_object = 'VirtualMachine' fault_list = ['Fault'] doc = mock.Mock() def side_effect(mo, **kwargs): self.assertEqual(managed_object, mo._type) self.assertEqual(managed_object, mo.value) fault = mock.Mock(faultstring="MyFault") fault_children = mock.Mock() fault_children.name = "name" fault_children.getText.return_value = "value" child = mock.Mock() child.get.return_value = fault_list[0] child.getChildren.return_value = [fault_children] detail = mock.Mock() detail.getChildren.return_value = [child] doc.childAtPath.side_effect = [None, detail] raise suds.WebFault(fault, doc) svc_obj = service.Service() service_mock = svc_obj.client.service setattr(service_mock, 'powerOn', side_effect) ex = self.assertRaises(exceptions.VimFaultException, svc_obj.powerOn, managed_object) self.assertEqual(fault_list, ex.fault_list) self.assertEqual({'name': 'value'}, ex.details) self.assertEqual("MyFault", ex.msg) exp_calls = [mock.call('/detail'), mock.call('/Envelope/Body/Fault/detail')] self.assertEqual(exp_calls, doc.childAtPath.call_args_list) def test_request_handler_with_security_error(self): managed_object = 'VirtualMachine' doc = mock.Mock() def side_effect(mo, **kwargs): self.assertEqual(managed_object, mo._type) self.assertEqual(managed_object, mo.value) fault = mock.Mock(faultstring="MyFault") fault_children = mock.Mock() fault_children.name = "name" fault_children.getText.return_value = "value" child = mock.Mock() child.get.return_value = 'vim25:SecurityError' child.getChildren.return_value = [fault_children] detail = mock.Mock() detail.getChildren.return_value = [child] doc.childAtPath.return_value = detail raise suds.WebFault(fault, doc) svc_obj = service.Service() service_mock = svc_obj.client.service setattr(service_mock, 'powerOn', side_effect) ex = self.assertRaises(exceptions.VimFaultException, svc_obj.powerOn, managed_object) self.assertEqual([exceptions.NOT_AUTHENTICATED], ex.fault_list) self.assertEqual({'name': 'value'}, ex.details) self.assertEqual("MyFault", ex.msg) doc.childAtPath.assert_called_once_with('/detail') def test_request_handler_with_attribute_error(self): managed_object = 'VirtualMachine' svc_obj = service.Service() # no powerOn method in Service service_mock = mock.Mock(spec=service.Service) svc_obj.client.service = service_mock self.assertRaises(exceptions.VimAttributeException, svc_obj.powerOn, managed_object) def test_request_handler_with_http_cannot_send_error(self): managed_object = 'VirtualMachine' def side_effect(mo, **kwargs): self.assertEqual(managed_object, mo._type) self.assertEqual(managed_object, mo.value) raise httplib.CannotSendRequest() svc_obj = service.Service() attr_name = 'powerOn' service_mock = svc_obj.client.service setattr(service_mock, attr_name, side_effect) self.assertRaises(exceptions.VimSessionOverLoadException, svc_obj.powerOn, managed_object) def test_request_handler_with_http_response_not_ready_error(self): managed_object = 'VirtualMachine' def side_effect(mo, **kwargs): self.assertEqual(managed_object, mo._type) self.assertEqual(managed_object, mo.value) raise httplib.ResponseNotReady() svc_obj = service.Service() attr_name = 'powerOn' service_mock = svc_obj.client.service setattr(service_mock, attr_name, side_effect) self.assertRaises(exceptions.VimSessionOverLoadException, svc_obj.powerOn, managed_object) def test_request_handler_with_http_cannot_send_header_error(self): managed_object = 'VirtualMachine' def side_effect(mo, **kwargs): self.assertEqual(managed_object, mo._type) self.assertEqual(managed_object, mo.value) raise httplib.CannotSendHeader() svc_obj = service.Service() attr_name = 'powerOn' service_mock = svc_obj.client.service setattr(service_mock, attr_name, side_effect) self.assertRaises(exceptions.VimSessionOverLoadException, svc_obj.powerOn, managed_object) 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 requests.ConnectionError() svc_obj = service.Service() attr_name = 'powerOn' service_mock = svc_obj.client.service setattr(service_mock, attr_name, side_effect) self.assertRaises(exceptions.VimConnectionException, svc_obj.powerOn, managed_object) def test_request_handler_with_http_error(self): managed_object = 'VirtualMachine' def side_effect(mo, **kwargs): self.assertEqual(managed_object, mo._type) self.assertEqual(managed_object, mo.value) raise requests.HTTPError() svc_obj = service.Service() attr_name = 'powerOn' service_mock = svc_obj.client.service setattr(service_mock, attr_name, side_effect) self.assertRaises(exceptions.VimConnectionException, svc_obj.powerOn, managed_object) @mock.patch.object(vim_util, 'get_moref', return_value=None) def test_request_handler_no_value(self, mock_moref): managed_object = 'VirtualMachine' svc_obj = service.Service() ret = svc_obj.UnregisterVM(managed_object) self.assertIsNone(ret) def _test_request_handler_with_exception(self, message, exception): managed_object = 'VirtualMachine' def side_effect(mo, **kwargs): self.assertEqual(managed_object, mo._type) self.assertEqual(managed_object, mo.value) raise Exception(message) svc_obj = service.Service() attr_name = 'powerOn' service_mock = svc_obj.client.service setattr(service_mock, attr_name, side_effect) self.assertRaises(exception, svc_obj.powerOn, managed_object) def test_request_handler_with_address_in_use_error(self): self._test_request_handler_with_exception( service.ADDRESS_IN_USE_ERROR, exceptions.VimSessionOverLoadException) def test_request_handler_with_conn_abort_error(self): self._test_request_handler_with_exception( service.CONN_ABORT_ERROR, exceptions.VimSessionOverLoadException) def test_request_handler_with_resp_not_xml_error(self): self._test_request_handler_with_exception( service.RESP_NOT_XML_ERROR, exceptions.VimSessionOverLoadException) def test_request_handler_with_generic_error(self): self._test_request_handler_with_exception( 'GENERIC_ERROR', exceptions.VimException) def test_get_session_cookie(self): svc_obj = service.Service() cookie_value = 'xyz' cookie = mock.Mock() cookie.name = 'vmware_soap_session' cookie.value = cookie_value svc_obj.client.options.transport.cookiejar = [cookie] self.assertEqual(cookie_value, svc_obj.get_http_cookie()) def test_get_session_cookie_with_no_cookie(self): svc_obj = service.Service() cookie = mock.Mock() cookie.name = 'cookie' cookie.value = 'xyz' svc_obj.client.options.transport.cookiejar = [cookie] self.assertIsNone(svc_obj.get_http_cookie()) def test_set_soap_headers(self): def fake_set_options(*args, **kwargs): headers = kwargs['soapheaders'] self.assertEqual(1, len(headers)) txt = headers[0].getText() self.assertEqual('fira-12345', txt) svc_obj = service.Service() svc_obj.client.options.soapheaders = None setattr(svc_obj.client, 'set_options', fake_set_options) svc_obj._set_soap_headers('fira-12345') def test_soap_headers_pbm(self): def fake_set_options(*args, **kwargs): headers = kwargs['soapheaders'] self.assertEqual(2, len(headers)) self.assertEqual('vc-session-cookie', headers[0].getText()) self.assertEqual('fira-12345', headers[1].getText()) svc_obj = service.Service() svc_obj._vc_session_cookie = 'vc-session-cookie' setattr(svc_obj.client, 'set_options', fake_set_options) svc_obj._set_soap_headers('fira-12345') class MemoryCacheTest(base.TestCase): """Test class for MemoryCache.""" def test_get_set(self): cache = service.MemoryCache() cache.put('key1', 'value1') cache.put('key2', 'value2') self.assertEqual('value1', cache.get('key1')) self.assertEqual('value2', cache.get('key2')) self.assertIsNone(cache.get('key3')) @mock.patch('suds.reader.DefinitionsReader.open') @mock.patch('suds.reader.DocumentReader.download', create=True) def test_shared_cache(self, mock_reader, mock_open): cache1 = service.Service().client.options.cache cache2 = service.Service().client.options.cache self.assertIs(cache1, cache2) @mock.patch('oslo_utils.timeutils.utcnow_ts') def test_cache_timeout(self, mock_utcnow_ts): mock_utcnow_ts.side_effect = [100, 125, 150, 175, 195, 200, 225] cache = service.MemoryCache() cache.put('key1', 'value1', 10) cache.put('key2', 'value2', 75) cache.put('key3', 'value3', 100) self.assertIsNone(cache.get('key1')) self.assertEqual('value2', cache.get('key2')) self.assertIsNone(cache.get('key2')) self.assertEqual('value3', cache.get('key3')) class RequestsTransportTest(base.TestCase): """Tests for RequestsTransport.""" def test_open(self): transport = service.RequestsTransport() data = b"Hello World" resp = mock.Mock(content=data) transport.session.get = mock.Mock(return_value=resp) request = mock.Mock(url=mock.sentinel.url) self.assertEqual(data, transport.open(request).getvalue()) transport.session.get.assert_called_once_with(mock.sentinel.url, verify=transport.verify) def test_send(self): transport = service.RequestsTransport() resp = mock.Mock(status_code=mock.sentinel.status_code, headers=mock.sentinel.headers, content=mock.sentinel.content) transport.session.post = mock.Mock(return_value=resp) request = mock.Mock(url=mock.sentinel.url, message=mock.sentinel.message, headers=mock.sentinel.req_headers) reply = transport.send(request) self.assertEqual(mock.sentinel.status_code, reply.code) self.assertEqual(mock.sentinel.headers, reply.headers) self.assertEqual(mock.sentinel.content, reply.message) def test_set_conn_pool_size(self): transport = service.RequestsTransport(pool_maxsize=100) local_file_adapter = transport.session.adapters['file:///'] self.assertEqual(100, local_file_adapter._pool_connections) self.assertEqual(100, local_file_adapter._pool_maxsize) https_adapter = transport.session.adapters['https://'] self.assertEqual(100, https_adapter._pool_connections) self.assertEqual(100, https_adapter._pool_maxsize) @mock.patch('os.path.getsize') def test_send_with_local_file_url(self, get_size_mock): transport = service.RequestsTransport() url = 'file:///foo' request = requests.PreparedRequest() request.url = url data = b"Hello World" get_size_mock.return_value = len(data) def readinto_mock(buf): buf[0:] = data if six.PY3: builtin_open = 'builtins.open' open_mock = mock.MagicMock(name='file_handle', spec=open) import _io file_spec = list(set(dir(_io.TextIOWrapper)).union( set(dir(_io.BytesIO)))) else: builtin_open = '__builtin__.open' open_mock = mock.MagicMock(name='file_handle', spec=file) file_spec = file file_handle = mock.MagicMock(spec=file_spec) file_handle.write.return_value = None file_handle.__enter__.return_value = file_handle file_handle.readinto.side_effect = readinto_mock open_mock.return_value = file_handle with mock.patch(builtin_open, open_mock, create=True): resp = transport.session.send(request) self.assertEqual(data, resp.content) def test_send_with_connection_timeout(self): transport = service.RequestsTransport(connection_timeout=120) request = mock.Mock(url=mock.sentinel.url, message=mock.sentinel.message, headers=mock.sentinel.req_headers) with mock.patch.object(transport.session, "post") as mock_post: transport.send(request) mock_post.assert_called_once_with( mock.sentinel.url, data=mock.sentinel.message, headers=mock.sentinel.req_headers, timeout=120, verify=transport.verify) class SudsLogFilterTest(base.TestCase): """Tests for SudsLogFilter.""" def setUp(self): super(SudsLogFilterTest, self).setUp() self.log_filter = service.SudsLogFilter() self.login = mock.Mock(spec=suds.sax.element.Element) self.username = suds.sax.element.Element('username').setText('admin') self.password = suds.sax.element.Element('password').setText( 'password') self.session_id = suds.sax.element.Element('session_id').setText( 'abcdef') def login_child_at_path_mock(path): if path == 'userName': return self.username if path == 'password': return self.password if path == 'sessionID': return self.session_id self.login.childAtPath.side_effect = login_child_at_path_mock def test_filter_with_no_child_at_path(self): message = mock.Mock(spec=object) record = mock.Mock(msg=message) self.assertTrue(self.log_filter.filter(record)) def test_filter_with_login_failure(self): message = mock.Mock(spec=suds.sax.element.Element) def child_at_path_mock(path): if path == '/Envelope/Body/Login': return self.login message.childAtPath.side_effect = child_at_path_mock record = mock.Mock(msg=message) self.assertTrue(self.log_filter.filter(record)) self.assertEqual('***', self.username.getText()) self.assertEqual('***', self.password.getText()) self.assertEqual('bcdef', self.session_id.getText()) def test_filter_with_session_is_active_failure(self): message = mock.Mock(spec=suds.sax.element.Element) def child_at_path_mock(path): if path == '/Envelope/Body/SessionIsActive': return self.login message.childAtPath.side_effect = child_at_path_mock record = mock.Mock(msg=message) self.assertTrue(self.log_filter.filter(record)) self.assertEqual('***', self.username.getText()) self.assertEqual('***', self.password.getText()) self.assertEqual('bcdef', self.session_id.getText()) def test_filter_with_unknown_failure(self): message = mock.Mock(spec=suds.sax.element.Element) def child_at_path_mock(path): return None message.childAtPath.side_effect = child_at_path_mock record = mock.Mock(msg=message) self.assertTrue(self.log_filter.filter(record)) self.assertEqual('admin', self.username.getText()) self.assertEqual('password', self.password.getText()) self.assertEqual('abcdef', self.session_id.getText()) oslo.vmware-2.26.0/oslo_vmware/tests/test_vim_util.py0000666000175100017510000005575713224676076023103 0ustar zuulzuul00000000000000# Copyright (c) 2014 VMware, Inc. # All Rights Reserved. # # 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. """ Unit tests for VMware API utility module. """ import collections import mock from oslo_vmware.tests import base from oslo_vmware import vim_util class VimUtilTest(base.TestCase): """Test class for utility methods in vim_util.""" def test_get_moref(self): moref = vim_util.get_moref("vm-0", "VirtualMachine") self.assertEqual("vm-0", moref.value) self.assertEqual("VirtualMachine", moref._type) def test_build_selection_spec(self): client_factory = mock.Mock() sel_spec = vim_util.build_selection_spec(client_factory, "test") self.assertEqual("test", sel_spec.name) def test_build_traversal_spec(self): client_factory = mock.Mock() sel_spec = mock.Mock() traversal_spec = vim_util.build_traversal_spec(client_factory, 'dc_to_hf', 'Datacenter', 'hostFolder', False, [sel_spec]) self.assertEqual("dc_to_hf", traversal_spec.name) self.assertEqual("hostFolder", traversal_spec.path) self.assertEqual([sel_spec], traversal_spec.selectSet) self.assertFalse(traversal_spec.skip) self.assertEqual("Datacenter", traversal_spec.type) @mock.patch.object(vim_util, 'build_selection_spec') def test_build_recursive_traversal_spec(self, build_selection_spec_mock): sel_spec = mock.Mock() rp_to_rp_sel_spec = mock.Mock() rp_to_vm_sel_spec = mock.Mock() def build_sel_spec_side_effect(client_factory, name): if name == 'visitFolders': return sel_spec elif name == 'rp_to_rp': return rp_to_rp_sel_spec elif name == 'rp_to_vm': return rp_to_vm_sel_spec else: return None build_selection_spec_mock.side_effect = build_sel_spec_side_effect traversal_spec_dict = {'dc_to_hf': {'type': 'Datacenter', 'path': 'hostFolder', 'skip': False, 'selectSet': [sel_spec]}, 'dc_to_vmf': {'type': 'Datacenter', 'path': 'vmFolder', 'skip': False, 'selectSet': [sel_spec]}, 'dc_to_netf': {'type': 'Datacenter', 'path': 'networkFolder', 'skip': False, 'selectSet': [sel_spec]}, 'h_to_vm': {'type': 'HostSystem', 'path': 'vm', 'skip': False, 'selectSet': [sel_spec]}, 'cr_to_h': {'type': 'ComputeResource', 'path': 'host', 'skip': False, 'selectSet': []}, 'cr_to_ds': {'type': 'ComputeResource', 'path': 'datastore', 'skip': False, 'selectSet': []}, 'cr_to_rp': {'type': 'ComputeResource', 'path': 'resourcePool', 'skip': False, 'selectSet': [rp_to_rp_sel_spec, rp_to_vm_sel_spec]}, 'cr_to_rp': {'type': 'ComputeResource', 'path': 'resourcePool', 'skip': False, 'selectSet': [rp_to_rp_sel_spec, rp_to_vm_sel_spec]}, 'ccr_to_h': {'type': 'ClusterComputeResource', 'path': 'host', 'skip': False, 'selectSet': []}, 'ccr_to_ds': {'type': 'ClusterComputeResource', 'path': 'datastore', 'skip': False, 'selectSet': []}, 'ccr_to_rp': {'type': 'ClusterComputeResource', 'path': 'resourcePool', 'skip': False, 'selectSet': [rp_to_rp_sel_spec, rp_to_vm_sel_spec]}, 'rp_to_rp': {'type': 'ResourcePool', 'path': 'resourcePool', 'skip': False, 'selectSet': [rp_to_rp_sel_spec, rp_to_vm_sel_spec]}, 'rp_to_vm': {'type': 'ResourcePool', 'path': 'vm', 'skip': False, 'selectSet': [rp_to_rp_sel_spec, rp_to_vm_sel_spec]}, } client_factory = mock.Mock() client_factory.create.side_effect = lambda ns: mock.Mock() trav_spec = vim_util.build_recursive_traversal_spec(client_factory) self.assertEqual("visitFolders", trav_spec.name) self.assertEqual("childEntity", trav_spec.path) self.assertFalse(trav_spec.skip) self.assertEqual("Folder", trav_spec.type) self.assertEqual(len(traversal_spec_dict) + 1, len(trav_spec.selectSet)) for spec in trav_spec.selectSet: if spec.name not in traversal_spec_dict: self.assertEqual(sel_spec, spec) else: exp_spec = traversal_spec_dict[spec.name] self.assertEqual(exp_spec['type'], spec.type) self.assertEqual(exp_spec['path'], spec.path) self.assertEqual(exp_spec['skip'], spec.skip) self.assertEqual(exp_spec['selectSet'], spec.selectSet) def test_build_property_spec(self): client_factory = mock.Mock() prop_spec = vim_util.build_property_spec(client_factory) self.assertFalse(prop_spec.all) self.assertEqual(["name"], prop_spec.pathSet) self.assertEqual("VirtualMachine", prop_spec.type) def test_build_object_spec(self): client_factory = mock.Mock() root_folder = mock.Mock() specs = [mock.Mock()] obj_spec = vim_util.build_object_spec(client_factory, root_folder, specs) self.assertEqual(root_folder, obj_spec.obj) self.assertEqual(specs, obj_spec.selectSet) self.assertFalse(obj_spec.skip) def test_build_property_filter_spec(self): client_factory = mock.Mock() prop_specs = [mock.Mock()] obj_specs = [mock.Mock()] filter_spec = vim_util.build_property_filter_spec(client_factory, prop_specs, obj_specs) self.assertEqual(obj_specs, filter_spec.objectSet) self.assertEqual(prop_specs, filter_spec.propSet) @mock.patch( 'oslo_vmware.vim_util.build_recursive_traversal_spec') def test_get_objects(self, build_recursive_traversal_spec): vim = mock.Mock() trav_spec = mock.Mock() build_recursive_traversal_spec.return_value = trav_spec max_objects = 10 _type = "VirtualMachine" def vim_RetrievePropertiesEx_side_effect(pc, specSet, options): self.assertTrue(pc is vim.service_content.propertyCollector) self.assertEqual(max_objects, options.maxObjects) self.assertEqual(1, len(specSet)) property_filter_spec = specSet[0] propSet = property_filter_spec.propSet self.assertEqual(1, len(propSet)) prop_spec = propSet[0] self.assertFalse(prop_spec.all) self.assertEqual(["name"], prop_spec.pathSet) self.assertEqual(_type, prop_spec.type) objSet = property_filter_spec.objectSet self.assertEqual(1, len(objSet)) obj_spec = objSet[0] self.assertTrue(obj_spec.obj is vim.service_content.rootFolder) self.assertEqual([trav_spec], obj_spec.selectSet) self.assertFalse(obj_spec.skip) vim.RetrievePropertiesEx.side_effect = ( vim_RetrievePropertiesEx_side_effect) vim_util.get_objects(vim, _type, max_objects) self.assertEqual(1, vim.RetrievePropertiesEx.call_count) def test_get_object_properties_with_empty_moref(self): vim = mock.Mock() ret = vim_util.get_object_properties(vim, None, None) self.assertIsNone(ret) @mock.patch('oslo_vmware.vim_util.cancel_retrieval') def test_get_object_properties(self, cancel_retrieval): vim = mock.Mock() moref = mock.Mock() moref._type = "VirtualMachine" retrieve_result = mock.Mock() def vim_RetrievePropertiesEx_side_effect(pc, specSet, options, skip_op_id=False): self.assertTrue(pc is vim.service_content.propertyCollector) self.assertEqual(1, options.maxObjects) self.assertEqual(1, len(specSet)) property_filter_spec = specSet[0] propSet = property_filter_spec.propSet self.assertEqual(1, len(propSet)) prop_spec = propSet[0] self.assertTrue(prop_spec.all) self.assertEqual(['name'], prop_spec.pathSet) self.assertEqual(moref._type, prop_spec.type) objSet = property_filter_spec.objectSet self.assertEqual(1, len(objSet)) obj_spec = objSet[0] self.assertEqual(moref, obj_spec.obj) self.assertEqual([], obj_spec.selectSet) self.assertFalse(obj_spec.skip) return retrieve_result vim.RetrievePropertiesEx.side_effect = ( vim_RetrievePropertiesEx_side_effect) res = vim_util.get_object_properties(vim, moref, None) self.assertEqual(1, vim.RetrievePropertiesEx.call_count) self.assertTrue(res is retrieve_result.objects) cancel_retrieval.assert_called_once_with(vim, retrieve_result) def test_get_token(self): retrieve_result = object() self.assertFalse(vim_util._get_token(retrieve_result)) @mock.patch('oslo_vmware.vim_util.get_object_properties') def test_get_object_properties_dict_empty(self, mock_obj_prop): mock_obj_prop.return_value = None vim = mock.Mock() moref = mock.Mock() res = vim_util.get_object_properties_dict(vim, moref, None) self.assertEqual({}, res) @mock.patch('oslo_vmware.vim_util.get_object_properties') def test_get_object_properties_dict(self, mock_obj_prop): expected_prop_dict = {'name': 'vm01'} mock_obj_content = mock.Mock() prop = mock.Mock() prop.name = "name" prop.val = "vm01" mock_obj_content.propSet = [prop] del mock_obj_content.missingSet mock_obj_prop.return_value = [mock_obj_content] vim = mock.Mock() moref = mock.Mock() res = vim_util.get_object_properties_dict(vim, moref, None) self.assertEqual(expected_prop_dict, res) @mock.patch('oslo_vmware.vim_util.get_object_properties') def test_get_object_properties_dict_missing(self, mock_obj_prop): mock_obj_content = mock.Mock() missing_prop = mock.Mock() missing_prop.path = "name" missing_prop.fault = mock.Mock() mock_obj_content.missingSet = [missing_prop] del mock_obj_content.propSet mock_obj_prop.return_value = [mock_obj_content] vim = mock.Mock() moref = mock.Mock() res = vim_util.get_object_properties_dict(vim, moref, None) self.assertEqual({}, res) @mock.patch('oslo_vmware.vim_util._get_token') def test_cancel_retrieval(self, get_token): token = mock.Mock() get_token.return_value = token vim = mock.Mock() retrieve_result = mock.Mock() vim_util.cancel_retrieval(vim, retrieve_result) get_token.assert_called_once_with(retrieve_result) vim.CancelRetrievePropertiesEx.assert_called_once_with( vim.service_content.propertyCollector, token=token) @mock.patch('oslo_vmware.vim_util._get_token') def test_continue_retrieval(self, get_token): token = mock.Mock() get_token.return_value = token vim = mock.Mock() retrieve_result = mock.Mock() vim_util.continue_retrieval(vim, retrieve_result) get_token.assert_called_once_with(retrieve_result) vim.ContinueRetrievePropertiesEx.assert_called_once_with( vim.service_content.propertyCollector, token=token) @mock.patch('oslo_vmware.vim_util.continue_retrieval') @mock.patch('oslo_vmware.vim_util.cancel_retrieval') def test_with_retrieval(self, cancel_retrieval, continue_retrieval): vim = mock.Mock() retrieve_result0 = mock.Mock() retrieve_result0.objects = [mock.Mock(), mock.Mock()] retrieve_result1 = mock.Mock() retrieve_result1.objects = [mock.Mock(), mock.Mock()] continue_retrieval.side_effect = [retrieve_result1, None] expected = retrieve_result0.objects + retrieve_result1.objects with vim_util.WithRetrieval(vim, retrieve_result0) as iterator: self.assertEqual(expected, list(iterator)) calls = [ mock.call(vim, retrieve_result0), mock.call(vim, retrieve_result1)] continue_retrieval.assert_has_calls(calls) self.assertFalse(cancel_retrieval.called) @mock.patch('oslo_vmware.vim_util.continue_retrieval') @mock.patch('oslo_vmware.vim_util.cancel_retrieval') def test_with_retrieval_early_exit(self, cancel_retrieval, continue_retrieval): vim = mock.Mock() retrieve_result = mock.Mock() with vim_util.WithRetrieval(vim, retrieve_result): pass cancel_retrieval.assert_called_once_with(vim, retrieve_result) @mock.patch('oslo_vmware.vim_util.get_object_properties') def test_get_object_property(self, get_object_properties): prop = mock.Mock() prop.val = "ubuntu-12.04" properties = mock.Mock() properties.propSet = [prop] properties_list = [properties] get_object_properties.return_value = properties_list vim = mock.Mock() moref = mock.Mock() property_name = 'name' val = vim_util.get_object_property(vim, moref, property_name) self.assertEqual(prop.val, val) get_object_properties.assert_called_once_with( vim, moref, [property_name], skip_op_id=False) def test_find_extension(self): vim = mock.Mock() ret = vim_util.find_extension(vim, 'fake-key') self.assertIsNotNone(ret) service_content = vim.service_content vim.FindExtension.assert_called_once_with( service_content.extensionManager, extensionKey='fake-key') def test_register_extension(self): vim = mock.Mock() ret = vim_util.register_extension(vim, 'fake-key', 'fake-type') self.assertIsNone(ret) service_content = vim.service_content vim.RegisterExtension.assert_called_once_with( service_content.extensionManager, extension=mock.ANY) def test_get_vc_version(self): session = mock.Mock() expected_version = '6.0.1' session.vim.service_content.about.version = expected_version version = vim_util.get_vc_version(session) self.assertEqual(expected_version, version) expected_version = '5.5' session.vim.service_content.about.version = expected_version version = vim_util.get_vc_version(session) self.assertEqual(expected_version, version) def test_get_inventory_path_folders(self): ObjectContent = collections.namedtuple('ObjectContent', ['propSet']) DynamicProperty = collections.namedtuple('Property', ['name', 'val']) obj1 = ObjectContent(propSet=[ DynamicProperty(name='Datacenter', val='dc-1'), ]) obj2 = ObjectContent(propSet=[ DynamicProperty(name='Datacenter', val='folder-2'), ]) obj3 = ObjectContent(propSet=[ DynamicProperty(name='Datacenter', val='folder-1'), ]) objects = ['foo', 'bar', obj1, obj2, obj3] result = mock.sentinel.objects result.objects = objects session = mock.Mock() session.vim.RetrievePropertiesEx = mock.Mock() session.vim.RetrievePropertiesEx.return_value = result entity = mock.Mock() inv_path = vim_util.get_inventory_path(session.vim, entity, 100) self.assertEqual('/folder-2/dc-1', inv_path) def test_get_inventory_path_no_folder(self): ObjectContent = collections.namedtuple('ObjectContent', ['propSet']) DynamicProperty = collections.namedtuple('Property', ['name', 'val']) obj1 = ObjectContent(propSet=[ DynamicProperty(name='Datacenter', val='dc-1'), ]) objects = ['foo', 'bar', obj1] result = mock.sentinel.objects result.objects = objects session = mock.Mock() session.vim.RetrievePropertiesEx = mock.Mock() session.vim.RetrievePropertiesEx.return_value = result entity = mock.Mock() inv_path = vim_util.get_inventory_path(session.vim, entity, 100) self.assertEqual('dc-1', inv_path) def test_get_prop_spec(self): client_factory = mock.Mock() prop_spec = vim_util.get_prop_spec( client_factory, "VirtualMachine", ["test_path"]) self.assertEqual(["test_path"], prop_spec.pathSet) self.assertEqual("VirtualMachine", prop_spec.type) def test_get_obj_spec(self): client_factory = mock.Mock() mock_obj = mock.Mock() obj_spec = vim_util.get_obj_spec( client_factory, mock_obj, select_set=["abc"]) self.assertEqual(mock_obj, obj_spec.obj) self.assertFalse(obj_spec.skip) self.assertEqual(["abc"], obj_spec.selectSet) def test_get_prop_filter_spec(self): client_factory = mock.Mock() mock_obj = mock.Mock() filter_spec = vim_util.get_prop_filter_spec( client_factory, [mock_obj], ["test_prop"]) self.assertEqual([mock_obj], filter_spec.objectSet) self.assertEqual(["test_prop"], filter_spec.propSet) @mock.patch('oslo_vmware.vim_util.get_prop_spec') @mock.patch('oslo_vmware.vim_util.get_obj_spec') @mock.patch('oslo_vmware.vim_util.get_prop_filter_spec') def _test_get_properties_for_a_collection_of_objects( self, objs, max_objects, mock_get_prop_filter_spec, mock_get_obj_spec, mock_get_prop_spec): vim = mock.Mock() if len(objs) == 0: self.assertEqual( [], vim_util.get_properties_for_a_collection_of_objects( vim, 'VirtualMachine', [], {})) return mock_prop_spec = mock.Mock() mock_get_prop_spec.return_value = mock_prop_spec mock_get_obj_spec.side_effect = [mock.Mock() for obj in objs] get_obj_spec_calls = [mock.call(vim.client.factory, obj) for obj in objs] mock_prop_spec = mock.Mock() mock_get_prop_spec.return_value = mock_prop_spec mock_prop_filter_spec = mock.Mock() mock_get_prop_filter_spec.return_value = mock_prop_filter_spec mock_options = mock.Mock() vim.client.factory.create.return_value = mock_options mock_return_value = mock.Mock() vim.RetrievePropertiesEx.return_value = mock_return_value res = vim_util.get_properties_for_a_collection_of_objects( vim, 'VirtualMachine', objs, ['runtime'], max_objects) self.assertEqual(mock_return_value, res) mock_get_prop_spec.assert_called_once_with(vim.client.factory, 'VirtualMachine', ['runtime']) self.assertEqual(get_obj_spec_calls, mock_get_obj_spec.mock_calls) vim.client.factory.create.assert_called_once_with( 'ns0:RetrieveOptions') self.assertEqual(max_objects if max_objects else len(objs), mock_options.maxObjects) vim.RetrievePropertiesEx.assert_called_once_with( vim.service_content.propertyCollector, specSet=[mock_prop_filter_spec], options=mock_options) def test_get_properties_for_a_collection_of_objects( self): objects = ["m1", "m2"] self._test_get_properties_for_a_collection_of_objects(objects, None) def test_get_properties_for_a_collection_of_objects_max_objects_1( self): objects = ["m1", "m2"] self._test_get_properties_for_a_collection_of_objects(objects, 1) def test_get_properties_for_a_collection_of_objects_no_objects( self): self._test_get_properties_for_a_collection_of_objects([], None) def test_propset_dict(self): self.assertEqual({}, vim_util.propset_dict(None)) mock_propset = [] for i in range(2): mock_obj = mock.Mock() mock_obj.name = "test_name_%d" % i mock_obj.val = "test_val_%d" % i mock_propset.append(mock_obj) self.assertEqual({"test_name_0": "test_val_0", "test_name_1": "test_val_1"}, vim_util.propset_dict(mock_propset)) oslo.vmware-2.26.0/oslo_vmware/tests/objects/0000775000175100017510000000000013224676314021241 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/oslo_vmware/tests/objects/test_datastore.py0000666000175100017510000004127013224676076024653 0ustar zuulzuul00000000000000# Copyright (c) 2014 VMware, Inc. # # 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. import mock from oslo_utils import units import six.moves.urllib.parse as urlparse from oslo_vmware import constants from oslo_vmware.objects import datastore from oslo_vmware.tests import base from oslo_vmware import vim_util class HostMount(object): def __init__(self, key, mountInfo): self.key = key self.mountInfo = mountInfo class MountInfo(object): def __init__(self, accessMode, mounted, accessible): self.accessMode = accessMode self.mounted = mounted self.accessible = accessible class DatastoreTestCase(base.TestCase): """Test the Datastore object.""" def test_ds(self): ds = datastore.Datastore( "fake_ref", "ds_name", 2 * units.Gi, 1 * units.Gi, 1 * units.Gi) self.assertEqual('ds_name', ds.name) self.assertEqual('fake_ref', ds.ref) self.assertEqual(2 * units.Gi, ds.capacity) self.assertEqual(1 * units.Gi, ds.freespace) self.assertEqual(1 * units.Gi, ds.uncommitted) def test_ds_invalid_space(self): self.assertRaises(ValueError, datastore.Datastore, "fake_ref", "ds_name", 1 * units.Gi, 2 * units.Gi) self.assertRaises(ValueError, datastore.Datastore, "fake_ref", "ds_name", None, 2 * units.Gi) def test_ds_no_capacity_no_freespace(self): ds = datastore.Datastore("fake_ref", "ds_name") self.assertIsNone(ds.capacity) self.assertIsNone(ds.freespace) def test_ds_invalid(self): self.assertRaises(ValueError, datastore.Datastore, None, "ds_name") self.assertRaises(ValueError, datastore.Datastore, "fake_ref", None) def test_build_path(self): ds = datastore.Datastore("fake_ref", "ds_name") ds_path = ds.build_path("some_dir", "foo.vmdk") self.assertEqual('[ds_name] some_dir/foo.vmdk', str(ds_path)) def test_build_url(self): ds = datastore.Datastore("fake_ref", "ds_name") path = 'images/ubuntu.vmdk' self.assertRaises(ValueError, ds.build_url, 'https', '10.0.0.2', path) ds.datacenter = mock.Mock() ds.datacenter.name = "dc_path" ds_url = ds.build_url('https', '10.0.0.2', path) self.assertEqual(ds_url.datastore_name, "ds_name") self.assertEqual(ds_url.datacenter_path, "dc_path") self.assertEqual(ds_url.path, path) def test_get_summary(self): ds_ref = vim_util.get_moref('ds-0', 'Datastore') ds = datastore.Datastore(ds_ref, 'ds-name') summary = mock.sentinel.summary session = mock.Mock() session.invoke_api = mock.Mock() session.invoke_api.return_value = summary ret = ds.get_summary(session) self.assertEqual(summary, ret) session.invoke_api.assert_called_once_with(vim_util, 'get_object_property', session.vim, ds.ref, 'summary') def _test_get_connected_hosts(self, in_maintenance_mode, m1_accessible=True): session = mock.Mock() ds_ref = vim_util.get_moref('ds-0', 'Datastore') ds = datastore.Datastore(ds_ref, 'ds-name') ds.get_summary = mock.Mock() ds.get_summary.return_value.accessible = False self.assertEqual([], ds.get_connected_hosts(session)) ds.get_summary.return_value.accessible = True m1 = HostMount("m1", MountInfo('readWrite', True, m1_accessible)) m2 = HostMount("m2", MountInfo('read', True, True)) m3 = HostMount("m3", MountInfo('readWrite', False, True)) m4 = HostMount("m4", MountInfo('readWrite', True, False)) ds.get_summary.assert_called_once_with(session) class Prop(object): DatastoreHostMount = [m1, m2, m3, m4] class HostRuntime(object): inMaintenanceMode = in_maintenance_mode class HostProp(object): name = 'runtime' val = HostRuntime() class Object(object): obj = "m1" propSet = [HostProp()] class Runtime(object): objects = [Object()] session.invoke_api = mock.Mock(side_effect=[Prop(), Runtime()]) hosts = ds.get_connected_hosts(session) calls = [mock.call(vim_util, 'get_object_property', session.vim, ds_ref, 'host')] if m1_accessible: calls.append( mock.call(vim_util, 'get_properties_for_a_collection_of_objects', session.vim, 'HostSystem', ["m1"], ['runtime'])) self.assertEqual(calls, session.invoke_api.mock_calls) return hosts def test_get_connected_hosts(self): hosts = self._test_get_connected_hosts(False) self.assertEqual(1, len(hosts)) self.assertEqual("m1", hosts.pop()) def test_get_connected_hosts_in_maintenance(self): hosts = self._test_get_connected_hosts(True) self.assertEqual(0, len(hosts)) def test_get_connected_hosts_ho_hosts(self): hosts = self._test_get_connected_hosts(False, False) self.assertEqual(0, len(hosts)) def test_is_datastore_mount_usable(self): m = MountInfo('readWrite', True, True) self.assertTrue(datastore.Datastore.is_datastore_mount_usable(m)) m = MountInfo('read', True, True) self.assertFalse(datastore.Datastore.is_datastore_mount_usable(m)) m = MountInfo('readWrite', False, True) self.assertFalse(datastore.Datastore.is_datastore_mount_usable(m)) m = MountInfo('readWrite', True, False) self.assertFalse(datastore.Datastore.is_datastore_mount_usable(m)) m = MountInfo('readWrite', False, False) self.assertFalse(datastore.Datastore.is_datastore_mount_usable(m)) m = MountInfo('readWrite', None, None) self.assertFalse(datastore.Datastore.is_datastore_mount_usable(m)) m = MountInfo('readWrite', None, True) self.assertFalse(datastore.Datastore.is_datastore_mount_usable(m)) class DatastorePathTestCase(base.TestCase): """Test the DatastorePath object.""" def test_ds_path(self): p = datastore.DatastorePath('dsname', 'a/b/c', 'file.iso') self.assertEqual('[dsname] a/b/c/file.iso', str(p)) self.assertEqual('a/b/c/file.iso', p.rel_path) self.assertEqual('a/b/c', p.parent.rel_path) self.assertEqual('[dsname] a/b/c', str(p.parent)) self.assertEqual('dsname', p.datastore) self.assertEqual('file.iso', p.basename) self.assertEqual('a/b/c', p.dirname) def test_ds_path_no_ds_name(self): bad_args = [ ('', ['a/b/c', 'file.iso']), (None, ['a/b/c', 'file.iso'])] for t in bad_args: self.assertRaises( ValueError, datastore.DatastorePath, t[0], *t[1]) def test_ds_path_invalid_path_components(self): bad_args = [ ('dsname', [None]), ('dsname', ['', None]), ('dsname', ['a', None]), ('dsname', ['a', None, 'b']), ('dsname', [None, '']), ('dsname', [None, 'b'])] for t in bad_args: self.assertRaises( ValueError, datastore.DatastorePath, t[0], *t[1]) def test_ds_path_no_subdir(self): args = [ ('dsname', ['', 'x.vmdk']), ('dsname', ['x.vmdk'])] canonical_p = datastore.DatastorePath('dsname', 'x.vmdk') self.assertEqual('[dsname] x.vmdk', str(canonical_p)) self.assertEqual('', canonical_p.dirname) self.assertEqual('x.vmdk', canonical_p.basename) self.assertEqual('x.vmdk', canonical_p.rel_path) for t in args: p = datastore.DatastorePath(t[0], *t[1]) self.assertEqual(str(canonical_p), str(p)) def test_ds_path_ds_only(self): args = [ ('dsname', []), ('dsname', ['']), ('dsname', ['', ''])] canonical_p = datastore.DatastorePath('dsname') self.assertEqual('[dsname]', str(canonical_p)) self.assertEqual('', canonical_p.rel_path) self.assertEqual('', canonical_p.basename) self.assertEqual('', canonical_p.dirname) for t in args: p = datastore.DatastorePath(t[0], *t[1]) self.assertEqual(str(canonical_p), str(p)) self.assertEqual(canonical_p.rel_path, p.rel_path) def test_ds_path_equivalence(self): args = [ ('dsname', ['a/b/c/', 'x.vmdk']), ('dsname', ['a/', 'b/c/', 'x.vmdk']), ('dsname', ['a', 'b', 'c', 'x.vmdk']), ('dsname', ['a/b/c', 'x.vmdk'])] canonical_p = datastore.DatastorePath('dsname', 'a/b/c', 'x.vmdk') for t in args: p = datastore.DatastorePath(t[0], *t[1]) self.assertEqual(str(canonical_p), str(p)) self.assertEqual(canonical_p.datastore, p.datastore) self.assertEqual(canonical_p.rel_path, p.rel_path) self.assertEqual(str(canonical_p.parent), str(p.parent)) def test_ds_path_non_equivalence(self): args = [ # leading slash ('dsname', ['/a', 'b', 'c', 'x.vmdk']), ('dsname', ['/a/b/c/', 'x.vmdk']), ('dsname', ['a/b/c', '/x.vmdk']), # leading space ('dsname', ['a/b/c/', ' x.vmdk']), ('dsname', ['a/', ' b/c/', 'x.vmdk']), ('dsname', [' a', 'b', 'c', 'x.vmdk']), # trailing space ('dsname', ['/a/b/c/', 'x.vmdk ']), ('dsname', ['a/b/c/ ', 'x.vmdk'])] canonical_p = datastore.DatastorePath('dsname', 'a/b/c', 'x.vmdk') for t in args: p = datastore.DatastorePath(t[0], *t[1]) self.assertNotEqual(str(canonical_p), str(p)) def test_equal(self): a = datastore.DatastorePath('ds_name', 'a') b = datastore.DatastorePath('ds_name', 'a') self.assertEqual(a, b) def test_join(self): p = datastore.DatastorePath('ds_name', 'a') ds_path = p.join('b') self.assertEqual('[ds_name] a/b', str(ds_path)) p = datastore.DatastorePath('ds_name', 'a') ds_path = p.join() bad_args = [ [None], ['', None], ['a', None], ['a', None, 'b']] for arg in bad_args: self.assertRaises(ValueError, p.join, *arg) def test_ds_path_parse(self): p = datastore.DatastorePath.parse('[dsname]') self.assertEqual('dsname', p.datastore) self.assertEqual('', p.rel_path) p = datastore.DatastorePath.parse('[dsname] folder') self.assertEqual('dsname', p.datastore) self.assertEqual('folder', p.rel_path) p = datastore.DatastorePath.parse('[dsname] folder/file') self.assertEqual('dsname', p.datastore) self.assertEqual('folder/file', p.rel_path) for p in [None, '']: self.assertRaises(ValueError, datastore.DatastorePath.parse, p) for p in ['bad path', '/a/b/c', 'a/b/c']: self.assertRaises(IndexError, datastore.DatastorePath.parse, p) class DatastoreURLTestCase(base.TestCase): """Test the DatastoreURL object.""" def test_path_strip(self): scheme = 'https' server = '13.37.73.31' path = 'images/ubuntu-14.04.vmdk' dc_path = 'datacenter-1' ds_name = 'datastore-1' params = {'dcPath': dc_path, 'dsName': ds_name} query = urlparse.urlencode(params) url = datastore.DatastoreURL(scheme, server, path, dc_path, ds_name) expected_url = '%s://%s/folder/%s?%s' % ( scheme, server, path, query) self.assertEqual(expected_url, str(url)) def test_path_lstrip(self): scheme = 'https' server = '13.37.73.31' path = '/images/ubuntu-14.04.vmdk' dc_path = 'datacenter-1' ds_name = 'datastore-1' params = {'dcPath': dc_path, 'dsName': ds_name} query = urlparse.urlencode(params) url = datastore.DatastoreURL(scheme, server, path, dc_path, ds_name) expected_url = '%s://%s/folder/%s?%s' % ( scheme, server, path.lstrip('/'), query) self.assertEqual(expected_url, str(url)) def test_path_rstrip(self): scheme = 'https' server = '13.37.73.31' path = 'images/ubuntu-14.04.vmdk/' dc_path = 'datacenter-1' ds_name = 'datastore-1' params = {'dcPath': dc_path, 'dsName': ds_name} query = urlparse.urlencode(params) url = datastore.DatastoreURL(scheme, server, path, dc_path, ds_name) expected_url = '%s://%s/folder/%s?%s' % ( scheme, server, path.rstrip('/'), query) self.assertEqual(expected_url, str(url)) def test_urlparse(self): dc_path = 'datacenter-1' ds_name = 'datastore-1' params = {'dcPath': dc_path, 'dsName': ds_name} query = urlparse.urlencode(params) url = 'https://13.37.73.31/folder/images/aa.vmdk?%s' % query ds_url = datastore.DatastoreURL.urlparse(url) self.assertEqual(url, str(ds_url)) def test_datastore_name(self): dc_path = 'datacenter-1' ds_name = 'datastore-1' params = {'dcPath': dc_path, 'dsName': ds_name} query = urlparse.urlencode(params) url = 'https://13.37.73.31/folder/images/aa.vmdk?%s' % query ds_url = datastore.DatastoreURL.urlparse(url) self.assertEqual(ds_name, ds_url.datastore_name) def test_datacenter_path(self): dc_path = 'datacenter-1' ds_name = 'datastore-1' params = {'dcPath': dc_path, 'dsName': ds_name} query = urlparse.urlencode(params) url = 'https://13.37.73.31/folder/images/aa.vmdk?%s' % query ds_url = datastore.DatastoreURL.urlparse(url) self.assertEqual(dc_path, ds_url.datacenter_path) def test_path(self): dc_path = 'datacenter-1' ds_name = 'datastore-1' params = {'dcPath': dc_path, 'dsName': ds_name} path = 'images/aa.vmdk' query = urlparse.urlencode(params) url = 'https://13.37.73.31/folder/%s?%s' % (path, query) ds_url = datastore.DatastoreURL.urlparse(url) self.assertEqual(path, ds_url.path) @mock.patch('six.moves.http_client.HTTPSConnection') def test_connect(self, mock_conn): dc_path = 'datacenter-1' ds_name = 'datastore-1' params = {'dcPath': dc_path, 'dsName': ds_name} query = urlparse.urlencode(params) url = 'https://13.37.73.31/folder/images/aa.vmdk?%s' % query ds_url = datastore.DatastoreURL.urlparse(url) cookie = mock.Mock() ds_url.connect('PUT', 128, cookie) mock_conn.assert_called_once_with('13.37.73.31') def test_get_transfer_ticket(self): dc_path = 'datacenter-1' ds_name = 'datastore-1' params = {'dcPath': dc_path, 'dsName': ds_name} query = urlparse.urlencode(params) url = 'https://13.37.73.31/folder/images/aa.vmdk?%s' % query session = mock.Mock() session.invoke_api = mock.Mock() class Ticket(object): id = 'fake_id' session.invoke_api.return_value = Ticket() ds_url = datastore.DatastoreURL.urlparse(url) ticket = ds_url.get_transfer_ticket(session, 'PUT') self.assertEqual('%s="%s"' % (constants.CGI_COOKIE_KEY, 'fake_id'), ticket) def test_get_datastore_by_ref(self): session = mock.Mock() ds_ref = mock.Mock() expected_props = {'summary.name': 'datastore1', 'summary.type': 'NFS', 'summary.freeSpace': 1000, 'summary.capacity': 2000} session.invoke_api = mock.Mock() session.invoke_api.return_value = expected_props ds_obj = datastore.get_datastore_by_ref(session, ds_ref) self.assertEqual(expected_props['summary.name'], ds_obj.name) self.assertEqual(expected_props['summary.type'], ds_obj.type) self.assertEqual(expected_props['summary.freeSpace'], ds_obj.freespace) self.assertEqual(expected_props['summary.capacity'], ds_obj.capacity) oslo.vmware-2.26.0/oslo_vmware/tests/objects/__init__.py0000666000175100017510000000000013224676076023347 0ustar zuulzuul00000000000000oslo.vmware-2.26.0/oslo_vmware/tests/objects/test_datacenter.py0000666000175100017510000000210513224676076024771 0ustar zuulzuul00000000000000# Copyright (c) 2014 VMware, Inc. # # 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. import mock from oslo_vmware.objects import datacenter from oslo_vmware.tests import base class DatacenterTestCase(base.TestCase): """Test the Datacenter object.""" def test_dc(self): self.assertRaises(ValueError, datacenter.Datacenter, None, 'dc-1') self.assertRaises(ValueError, datacenter.Datacenter, mock.Mock(), None) dc = datacenter.Datacenter('ref', 'name') self.assertEqual('ref', dc.ref) self.assertEqual('name', dc.name) oslo.vmware-2.26.0/oslo_vmware/constants.py0000666000175100017510000000217613224676076021051 0ustar zuulzuul00000000000000# Copyright (c) 2014 VMware, Inc. # All Rights Reserved. # # 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. """ Shared constants across the VMware ecosystem. """ # Datacenter path for HTTP access to datastores if the target server is an ESX/ # ESXi system: http://goo.gl/B5Htr8 for more information. ESX_DATACENTER_PATH = 'ha-datacenter' # User Agent for HTTP requests between OpenStack and vCenter. USER_AGENT = 'OpenStack-ESX-Adapter' # Key of the cookie header when using a SOAP session. SOAP_COOKIE_KEY = 'vmware_soap_session' # Key of the cookie header when using a CGI session. CGI_COOKIE_KEY = 'vmware_cgi_ticket' oslo.vmware-2.26.0/oslo_vmware/vim_util.py0000666000175100017510000005751113224676076020670 0ustar zuulzuul00000000000000# Copyright (c) 2014 VMware, Inc. # All Rights Reserved. # # 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. """ The VMware API utility module. """ import logging from oslo_utils import timeutils from suds import sudsobject LOG = logging.getLogger(__name__) def get_moref(value, type_): """Get managed object reference. :param value: value of the managed object :param type_: type of the managed object :returns: managed object reference with given value and type """ moref = sudsobject.Property(value) moref._type = type_ return moref def build_selection_spec(client_factory, name): """Builds the selection spec. :param client_factory: factory to get API input specs :param name: name for the selection spec :returns: selection spec """ sel_spec = client_factory.create('ns0:SelectionSpec') sel_spec.name = name return sel_spec def build_traversal_spec(client_factory, name, type_, path, skip, select_set): """Builds the traversal spec. :param client_factory: factory to get API input specs :param name: name for the traversal spec :param type_: type of the managed object :param path: property path of the managed object :param skip: whether or not to filter the object identified by param path :param select_set: set of selection specs specifying additional objects to filter :returns: traversal spec """ traversal_spec = client_factory.create('ns0:TraversalSpec') traversal_spec.name = name traversal_spec.type = type_ traversal_spec.path = path traversal_spec.skip = skip traversal_spec.selectSet = select_set return traversal_spec def build_recursive_traversal_spec(client_factory): """Builds recursive traversal spec to traverse managed object hierarchy. :param client_factory: factory to get API input specs :returns: recursive traversal spec """ visit_folders_select_spec = build_selection_spec(client_factory, 'visitFolders') # Next hop from Datacenter dc_to_hf = build_traversal_spec(client_factory, 'dc_to_hf', 'Datacenter', 'hostFolder', False, [visit_folders_select_spec]) dc_to_vmf = build_traversal_spec(client_factory, 'dc_to_vmf', 'Datacenter', 'vmFolder', False, [visit_folders_select_spec]) dc_to_netf = build_traversal_spec(client_factory, 'dc_to_netf', 'Datacenter', 'networkFolder', False, [visit_folders_select_spec]) # Next hop from HostSystem h_to_vm = build_traversal_spec(client_factory, 'h_to_vm', 'HostSystem', 'vm', False, [visit_folders_select_spec]) # Next hop from ComputeResource cr_to_h = build_traversal_spec(client_factory, 'cr_to_h', 'ComputeResource', 'host', False, []) cr_to_ds = build_traversal_spec(client_factory, 'cr_to_ds', 'ComputeResource', 'datastore', False, []) rp_to_rp_select_spec = build_selection_spec(client_factory, 'rp_to_rp') rp_to_vm_select_spec = build_selection_spec(client_factory, 'rp_to_vm') cr_to_rp = build_traversal_spec(client_factory, 'cr_to_rp', 'ComputeResource', 'resourcePool', False, [rp_to_rp_select_spec, rp_to_vm_select_spec]) # Next hop from ClusterComputeResource ccr_to_h = build_traversal_spec(client_factory, 'ccr_to_h', 'ClusterComputeResource', 'host', False, []) ccr_to_ds = build_traversal_spec(client_factory, 'ccr_to_ds', 'ClusterComputeResource', 'datastore', False, []) ccr_to_rp = build_traversal_spec(client_factory, 'ccr_to_rp', 'ClusterComputeResource', 'resourcePool', False, [rp_to_rp_select_spec, rp_to_vm_select_spec]) # Next hop from ResourcePool rp_to_rp = build_traversal_spec(client_factory, 'rp_to_rp', 'ResourcePool', 'resourcePool', False, [rp_to_rp_select_spec, rp_to_vm_select_spec]) rp_to_vm = build_traversal_spec(client_factory, 'rp_to_vm', 'ResourcePool', 'vm', False, [rp_to_rp_select_spec, rp_to_vm_select_spec]) # Get the assorted traversal spec which takes care of the objects to # be searched for from the rootFolder traversal_spec = build_traversal_spec(client_factory, 'visitFolders', 'Folder', 'childEntity', False, [visit_folders_select_spec, h_to_vm, dc_to_hf, dc_to_vmf, dc_to_netf, cr_to_ds, cr_to_h, cr_to_rp, ccr_to_h, ccr_to_ds, ccr_to_rp, rp_to_rp, rp_to_vm]) return traversal_spec def build_property_spec(client_factory, type_='VirtualMachine', properties_to_collect=None, all_properties=False): """Builds the property spec. :param client_factory: factory to get API input specs :param type_: type of the managed object :param properties_to_collect: names of the managed object properties to be collected while traversal filtering :param all_properties: whether all properties of the managed object need to be collected :returns: property spec """ if not properties_to_collect: properties_to_collect = ['name'] property_spec = client_factory.create('ns0:PropertySpec') property_spec.all = all_properties property_spec.pathSet = properties_to_collect property_spec.type = type_ return property_spec def build_object_spec(client_factory, root_folder, traversal_specs): """Builds the object spec. :param client_factory: factory to get API input specs :param root_folder: root folder reference; the starting point of traversal :param traversal_specs: filter specs required for traversal :returns: object spec """ object_spec = client_factory.create('ns0:ObjectSpec') object_spec.obj = root_folder object_spec.skip = False object_spec.selectSet = traversal_specs return object_spec def build_property_filter_spec(client_factory, property_specs, object_specs): """Builds the property filter spec. :param client_factory: factory to get API input specs :param property_specs: property specs to be collected for filtered objects :param object_specs: object specs to identify objects to be filtered :returns: property filter spec """ property_filter_spec = client_factory.create('ns0:PropertyFilterSpec') property_filter_spec.propSet = property_specs property_filter_spec.objectSet = object_specs return property_filter_spec def get_objects(vim, type_, max_objects, properties_to_collect=None, all_properties=False): """Get all managed object references of the given type. It is the caller's responsibility to continue or cancel retrieval. :param vim: Vim object :param type_: type of the managed object :param max_objects: maximum number of objects that should be returned in a single call :param properties_to_collect: names of the managed object properties to be collected :param all_properties: whether all properties of the managed object need to be collected :returns: all managed object references of the given type :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException, VimConnectionException """ if not properties_to_collect: properties_to_collect = ['name'] client_factory = vim.client.factory recur_trav_spec = build_recursive_traversal_spec(client_factory) object_spec = build_object_spec(client_factory, vim.service_content.rootFolder, [recur_trav_spec]) property_spec = build_property_spec( client_factory, type_=type_, properties_to_collect=properties_to_collect, all_properties=all_properties) property_filter_spec = build_property_filter_spec(client_factory, [property_spec], [object_spec]) options = client_factory.create('ns0:RetrieveOptions') options.maxObjects = max_objects return vim.RetrievePropertiesEx(vim.service_content.propertyCollector, specSet=[property_filter_spec], options=options) def get_object_properties(vim, moref, properties_to_collect, skip_op_id=False): """Get properties of the given managed object. :param vim: Vim object :param moref: managed object reference :param properties_to_collect: names of the managed object properties to be collected :param skip_op_id: whether to skip putting opID in the request :returns: properties of the given managed object :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException, VimConnectionException """ if moref is None: return None client_factory = vim.client.factory all_properties = (properties_to_collect is None or len(properties_to_collect) == 0) property_spec = build_property_spec( client_factory, type_=moref._type, properties_to_collect=properties_to_collect, all_properties=all_properties) object_spec = build_object_spec(client_factory, moref, []) property_filter_spec = build_property_filter_spec(client_factory, [property_spec], [object_spec]) options = client_factory.create('ns0:RetrieveOptions') options.maxObjects = 1 retrieve_result = vim.RetrievePropertiesEx( vim.service_content.propertyCollector, specSet=[property_filter_spec], options=options, skip_op_id=skip_op_id) cancel_retrieval(vim, retrieve_result) return retrieve_result.objects def get_object_properties_dict(vim, moref, properties_to_collect): """Get properties of the given managed object as a dict. :param vim: Vim object :param moref: managed object reference :param properties_to_collect: names of the managed object properties to be collected :returns: a dict of properties of the given managed object :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException, VimConnectionException """ obj_contents = get_object_properties(vim, moref, properties_to_collect) if obj_contents is None: return {} property_dict = {} if hasattr(obj_contents[0], 'propSet'): dynamic_properties = obj_contents[0].propSet if dynamic_properties: for prop in dynamic_properties: property_dict[prop.name] = prop.val # The object may have information useful for logging if hasattr(obj_contents[0], 'missingSet'): for m in obj_contents[0].missingSet: LOG.warning("Unable to retrieve value for %(path)s " "Reason: %(reason)s", {'path': m.path, 'reason': m.fault.localizedMessage}) return property_dict def _get_token(retrieve_result): """Get token from result to obtain next set of results. :retrieve_result: Result of RetrievePropertiesEx API call :returns: token to obtain next set of results; None if no more results. """ return getattr(retrieve_result, 'token', None) def cancel_retrieval(vim, retrieve_result): """Cancels the retrieve operation if necessary. :param vim: Vim object :param retrieve_result: result of RetrievePropertiesEx API call :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException, VimConnectionException """ token = _get_token(retrieve_result) if token: collector = vim.service_content.propertyCollector vim.CancelRetrievePropertiesEx(collector, token=token) def continue_retrieval(vim, retrieve_result): """Continue retrieving results, if available. :param vim: Vim object :param retrieve_result: result of RetrievePropertiesEx API call :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException, VimConnectionException """ token = _get_token(retrieve_result) if token: collector = vim.service_content.propertyCollector return vim.ContinueRetrievePropertiesEx(collector, token=token) class WithRetrieval(object): """Context to retrieve results. This context provides an iterator to retrieve results and cancel (when needed) retrieve operation on __exit__. Example: with WithRetrieval(vim, retrieve_result) as objects: for obj in objects: # Use obj """ def __init__(self, vim, retrieve_result): super(WithRetrieval, self).__init__() self.vim = vim self.retrieve_result = retrieve_result def __enter__(self): return iter(self) def __exit__(self, exc_type, exc_value, traceback): if self.retrieve_result: cancel_retrieval(self.vim, self.retrieve_result) def __iter__(self): while self.retrieve_result: for obj in self.retrieve_result.objects: yield obj self.retrieve_result = continue_retrieval( self.vim, self.retrieve_result) def get_object_property(vim, moref, property_name, skip_op_id=False): """Get property of the given managed object. :param vim: Vim object :param moref: managed object reference :param property_name: name of the property to be retrieved :param skip_op_id: whether to skip putting opID in the request :returns: property of the given managed object :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException, VimConnectionException """ props = get_object_properties(vim, moref, [property_name], skip_op_id=skip_op_id) prop_val = None if props: prop = None if hasattr(props[0], 'propSet'): # propSet will be set only if the server provides value # for the field prop = props[0].propSet if prop: prop_val = prop[0].val return prop_val def find_extension(vim, key): """Looks for an existing extension. :param vim: Vim object :param key: the key to search for :returns: the data object Extension or None """ extension_manager = vim.service_content.extensionManager return vim.FindExtension(extension_manager, extensionKey=key) def register_extension(vim, key, type, label='OpenStack', summary='OpenStack services', version='1.0'): """Create a new extention. :param vim: Vim object :param key: the key for the extension :param type: Managed entity type, as defined by the extension. This matches the type field in the configuration about a virtual machine or vApp :param label: Display label :param summary: Summary description :param version: Extension version number as a dot-separated string """ extension_manager = vim.service_content.extensionManager client_factory = vim.client.factory os_ext = client_factory.create('ns0:Extension') os_ext.key = key entity_info = client_factory.create('ns0:ExtManagedEntityInfo') entity_info.type = type os_ext.managedEntityInfo = [entity_info] os_ext.version = version desc = client_factory.create('ns0:Description') desc.label = label desc.summary = summary os_ext.description = desc os_ext.lastHeartbeatTime = timeutils.utcnow().isoformat() vim.RegisterExtension(extension_manager, extension=os_ext) def get_vc_version(session): """Return the dot-separated vCenter version string. For example, "1.2". :param session: vCenter soap session :return: vCenter version """ return session.vim.service_content.about.version def get_inventory_path(vim, entity_ref, max_objects=100): """Get the inventory path of a managed entity. :param vim: Vim object :param entity_ref: managed entity reference :param max_objects: maximum number of objects that should be returned in a single call :return: inventory path of the entity_ref """ client_factory = vim.client.factory property_collector = vim.service_content.propertyCollector prop_spec = build_property_spec(client_factory, 'ManagedEntity', ['name', 'parent']) select_set = build_selection_spec(client_factory, 'ParentTraversalSpec') select_set = build_traversal_spec( client_factory, 'ParentTraversalSpec', 'ManagedEntity', 'parent', False, [select_set]) obj_spec = build_object_spec(client_factory, entity_ref, select_set) prop_filter_spec = build_property_filter_spec(client_factory, [prop_spec], [obj_spec]) options = client_factory.create('ns0:RetrieveOptions') options.maxObjects = max_objects retrieve_result = vim.RetrievePropertiesEx( property_collector, specSet=[prop_filter_spec], options=options) entity_name = None propSet = None path = "" with WithRetrieval(vim, retrieve_result) as objects: for obj in objects: if hasattr(obj, 'propSet'): propSet = obj.propSet if len(propSet) >= 1 and not entity_name: entity_name = propSet[0].val elif len(propSet) >= 1: path = '%s/%s' % (propSet[0].val, path) # NOTE(arnaud): slice to exclude the root folder from the result. if propSet is not None and len(propSet) > 0: path = path[len(propSet[0].val):] if entity_name is None: entity_name = "" return '%s%s' % (path, entity_name) def get_http_service_request_spec(client_factory, method, uri): """Build a HTTP service request spec. :param client_factory: factory to get API input specs :param method: HTTP method (GET, POST, PUT) :param uri: target URL """ http_service_request_spec = client_factory.create( 'ns0:SessionManagerHttpServiceRequestSpec') http_service_request_spec.method = method http_service_request_spec.url = uri return http_service_request_spec def get_prop_spec(client_factory, spec_type, properties): """Builds the Property Spec Object.""" prop_spec = client_factory.create('ns0:PropertySpec') prop_spec.type = spec_type prop_spec.pathSet = properties return prop_spec def get_obj_spec(client_factory, obj, select_set=None): """Builds the Object Spec object.""" obj_spec = client_factory.create('ns0:ObjectSpec') obj_spec.obj = obj obj_spec.skip = False if select_set is not None: obj_spec.selectSet = select_set return obj_spec def get_prop_filter_spec(client_factory, obj_spec, prop_spec): """Builds the Property Filter Spec Object.""" prop_filter_spec = client_factory.create('ns0:PropertyFilterSpec') prop_filter_spec.propSet = prop_spec prop_filter_spec.objectSet = obj_spec return prop_filter_spec def get_properties_for_a_collection_of_objects(vim, type_, obj_list, properties, max_objects=None): """Gets the list of properties for the collection of objects of the type specified. """ client_factory = vim.client.factory if len(obj_list) == 0: return [] prop_spec = get_prop_spec(client_factory, type_, properties) lst_obj_specs = [] for obj in obj_list: lst_obj_specs.append(get_obj_spec(client_factory, obj)) prop_filter_spec = get_prop_filter_spec(client_factory, lst_obj_specs, [prop_spec]) options = client_factory.create('ns0:RetrieveOptions') options.maxObjects = max_objects if max_objects else len(obj_list) return vim.RetrievePropertiesEx( vim.service_content.propertyCollector, specSet=[prop_filter_spec], options=options) def propset_dict(propset): """Turn a propset list into a dictionary PropSet is an optional attribute on ObjectContent objects that are returned by the VMware API. You can read more about these at: | http://pubs.vmware.com/vsphere-51/index.jsp | #com.vmware.wssdk.apiref.doc/ | vmodl.query.PropertyCollector.ObjectContent.html :param propset: a property "set" from ObjectContent :return: dictionary representing property set """ if propset is None: return {} return {prop.name: prop.val for prop in propset} oslo.vmware-2.26.0/oslo_vmware/vim.py0000666000175100017510000000471413224676076017630 0ustar zuulzuul00000000000000# Copyright (c) 2014 VMware, Inc. # All Rights Reserved. # # 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_vmware import service 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, cacert=None, insecure=True, pool_maxsize=10, connection_timeout=None, op_id_prefix='oslo.vmware'): """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 :param pool_maxsize: Maximum number of connections in http connection pool :param connection_timeout: Maximum time in seconds to wait for peer to respond. :param op_id_prefix: String prefix for the operation ID. :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException, VimConnectionException """ base_url = service.Service.build_base_url(protocol, host, port) soap_url = base_url + '/sdk' if wsdl_url is None: wsdl_url = soap_url + '/vimService.wsdl' super(Vim, self).__init__(wsdl_url, soap_url, cacert, insecure, pool_maxsize, connection_timeout, op_id_prefix) def retrieve_service_content(self): return self.RetrieveServiceContent(service.SERVICE_INSTANCE) def __repr__(self): return "VIM Object" def __str__(self): return "VIM Object" oslo.vmware-2.26.0/oslo_vmware/wsdl/0000775000175100017510000000000013224676314017417 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/oslo_vmware/wsdl/5.5/0000775000175100017510000000000013224676314017726 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/oslo_vmware/wsdl/5.5/pbm-messagetypes.xsd0000666000175100017510000001526413224676076023752 0ustar zuulzuul00000000000000 oslo.vmware-2.26.0/oslo_vmware/wsdl/5.5/pbm.wsdl0000666000175100017510000011606013224676076021412 0ustar zuulzuul00000000000000 oslo.vmware-2.26.0/oslo_vmware/wsdl/5.5/pbm-types.xsd0000666000175100017510000007127413224676076022410 0ustar zuulzuul00000000000000 oslo.vmware-2.26.0/oslo_vmware/wsdl/5.5/pbmService.wsdl0000666000175100017510000000104313224676076022725 0ustar zuulzuul00000000000000 oslo.vmware-2.26.0/oslo_vmware/wsdl/5.5/core-types.xsd0000666000175100017510000001774313224676076022563 0ustar zuulzuul00000000000000 oslo.vmware-2.26.0/oslo_vmware/wsdl/6.0/0000775000175100017510000000000013224676314017722 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/oslo_vmware/wsdl/6.0/pbm-messagetypes.xsd0000666000175100017510000002043713224676076023744 0ustar zuulzuul00000000000000 oslo.vmware-2.26.0/oslo_vmware/wsdl/6.0/pbm.wsdl0000666000175100017510000014503713224676076021414 0ustar zuulzuul00000000000000 oslo.vmware-2.26.0/oslo_vmware/wsdl/6.0/pbm-types.xsd0000666000175100017510000010077513224676076022403 0ustar zuulzuul00000000000000 oslo.vmware-2.26.0/oslo_vmware/wsdl/6.0/pbmService.wsdl0000666000175100017510000000105313224676076022722 0ustar zuulzuul00000000000000 oslo.vmware-2.26.0/oslo_vmware/wsdl/6.0/core-types.xsd0000666000175100017510000001743613224676076022556 0ustar zuulzuul00000000000000 oslo.vmware-2.26.0/oslo_vmware/wsdl/6.5/0000775000175100017510000000000013224676314017727 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/oslo_vmware/wsdl/6.5/query-types.xsd0000666000175100017510000002325213224676076022771 0ustar zuulzuul00000000000000 oslo.vmware-2.26.0/oslo_vmware/wsdl/6.5/pbm-messagetypes.xsd0000666000175100017510000002372613224676076023755 0ustar zuulzuul00000000000000 oslo.vmware-2.26.0/oslo_vmware/wsdl/6.5/vim-types.xsd0000666000175100017510000506256113224676076022432 0ustar zuulzuul00000000000000 oslo.vmware-2.26.0/oslo_vmware/wsdl/6.5/reflect-types.xsd0000666000175100017510000000063413224676076023247 0ustar zuulzuul00000000000000 oslo.vmware-2.26.0/oslo_vmware/wsdl/6.5/pbm.wsdl0000666000175100017510000063631613224676076021426 0ustar zuulzuul00000000000000 oslo.vmware-2.26.0/oslo_vmware/wsdl/6.5/pbm-types.xsd0000666000175100017510000011747713224676076022417 0ustar zuulzuul00000000000000 oslo.vmware-2.26.0/oslo_vmware/wsdl/6.5/pbmService.wsdl0000666000175100017510000000103313224676076022725 0ustar zuulzuul00000000000000 oslo.vmware-2.26.0/oslo_vmware/wsdl/6.5/core-types.xsd0000666000175100017510000002175613224676076022563 0ustar zuulzuul00000000000000 oslo.vmware-2.26.0/oslo_vmware/locale/0000775000175100017510000000000013224676314017705 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/oslo_vmware/locale/en_GB/0000775000175100017510000000000013224676314020657 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/oslo_vmware/locale/en_GB/LC_MESSAGES/0000775000175100017510000000000013224676314022444 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/oslo_vmware/locale/en_GB/LC_MESSAGES/oslo_vmware.po0000666000175100017510000001326013224676076025352 0ustar zuulzuul00000000000000# Andi Chandler , 2016. #zanata # Andreas Jaeger , 2016. #zanata # Andi Chandler , 2017. #zanata msgid "" msgstr "" "Project-Id-Version: oslo.vmware 2.23.1.dev6\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2017-09-20 20:53+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2017-09-28 03:30+0000\n" "Last-Translator: Andi Chandler \n" "Language-Team: English (United Kingdom)\n" "Language: en-GB\n" "X-Generator: Zanata 3.9.6\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" msgid "An unknown exception occurred." msgstr "An unknown exception occurred." msgid "Cannot delete file." msgstr "Cannot delete file." msgid "Capacity is smaller than free space" msgstr "Capacity is smaller than free space" #, python-format msgid "Connection error occurred while writing data to %s." msgstr "Connection error occurred while writing data to %s." msgid "Could not retrieve VMDK URL from lease info." msgstr "Could not retrieve VMDK URL from lease info." #, python-format msgid "" "Current session: %(session)s is inactive; re-creating the session while " "invoking method %(module)s.%(method)s." msgstr "" "Current session: %(session)s is inactive; re-creating the session while " "invoking method %(module)s.%(method)s." msgid "Datacenter name cannot be None" msgstr "Datacentre name cannot be None" msgid "Datacenter reference cannot be None" msgstr "Datacentre reference cannot be None" msgid "Datastore name cannot be None" msgstr "Datastore name cannot be None" msgid "Datastore name cannot be empty" msgstr "Datastore name cannot be empty" msgid "Datastore path cannot be empty" msgstr "Datastore path cannot be empty" msgid "Datastore reference cannot be None" msgstr "Datastore reference cannot be None" msgid "Duplicate name." msgstr "Duplicate name." msgid "Entity has another operation in process." msgstr "Entity has another operation in process." msgid "Error occurred while calling RetrievePropertiesEx." msgstr "Error occurred while calling RetrievePropertiesEx." #, python-format msgid "" "Error occurred while creating HTTP connection to write to VMDK file with URL " "= %s." msgstr "" "Error occurred while creating HTTP connection to write to VMDK file with URL " "= %s." #, python-format msgid "" "Error occurred while creating HTTP connection to write to file with URL = %s." msgstr "" "Error occurred while creating HTTP connection to write to file with URL = %s." #, python-format msgid "Error occurred while opening URL: %s for reading." msgstr "Error occurred while opening URL: %s for reading." #, python-format msgid "Error occurred while reading data from %s." msgstr "Error occurred while reading data from %s." #, python-format msgid "Error occurred while writing data to %s." msgstr "Error occurred while writing data to %s." #, python-format msgid "Error, read_handle: \"%(src)s\", write_handle: \"%(dest)s\"" msgstr "Error, read_handle: \"%(src)s\", write_handle: \"%(dest)s\"" #, python-format msgid "Exception in %s." msgstr "Exception in %s." msgid "File already exists." msgstr "File already exists." msgid "File fault." msgstr "File fault." msgid "File locked." msgstr "File locked." msgid "File not found." msgstr "File not found." msgid "Insufficient disk space." msgstr "Insufficient disk space." msgid "Invalid capacity" msgstr "Invalid capacity" msgid "Invalid power state." msgstr "Invalid power state." msgid "Invalid property." msgstr "Invalid property." #, python-format msgid "Invalid scheme: %s." msgstr "Invalid scheme: %s." #, python-format msgid "Lease: %(lease)s is in error state. Details: %(error_msg)s." msgstr "Lease: %(lease)s is in error state. Details: %(error_msg)s." msgid "Managed object not found." msgstr "Managed object not found." #, python-format msgid "Missing parameter : %(param)s" msgstr "Missing parameter : %(param)s" msgid "No Permission." msgstr "No Permission." msgid "No default value for use_linked_clone found." msgstr "No default value for use_linked_clone found." #, python-format msgid "No such SOAP method %s." msgstr "No such SOAP method %s." #, python-format msgid "No vmdk found in the OVA image %s." msgstr "No vmdk found in the OVA image %s." msgid "Not Authenticated." msgstr "Not Authenticated." msgid "Path component cannot be None" msgstr "Path component cannot be None" msgid "Resource already exists." msgstr "Resource already exists." #, python-format msgid "Socket error in %s." msgstr "Socket error in %s." #, python-format msgid "Timeout, read_handle: \"%(src)s\", write_handle: \"%(dest)s\"" msgstr "Timeout, read_handle: \"%(src)s\", write_handle: \"%(dest)s\"" #, python-format msgid "Type error in %s." msgstr "Type error in %s." #, python-format msgid "Unknown state: %(state)s for lease: %(lease)s." msgstr "Unknown state: %(state)s for lease: %(lease)s." msgid "VMware Driver configuration fault." msgstr "VMware Driver configuration fault." msgid "VMware Tools is not running." msgstr "VMware Tools is not running." msgid "cause" msgstr "cause" msgid "datacenter must be set to build url" msgstr "datacentre must be set to build url" msgid "details must be a dict" msgstr "details must be a dict" msgid "exception message must not be a list" msgstr "exception message must not be a list" msgid "exception should be a subclass of VimException" msgstr "exception should be a subclass of VimException" msgid "fault_list must be a list" msgstr "fault_list must be a list" #, python-format msgid "httplib error in %s." msgstr "httplib error in %s." #, python-format msgid "requests error in %s." msgstr "requests error in %s." msgid "string" msgstr "string" oslo.vmware-2.26.0/oslo_vmware/locale/fr/0000775000175100017510000000000013224676314020314 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/oslo_vmware/locale/fr/LC_MESSAGES/0000775000175100017510000000000013224676314022101 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/oslo_vmware/locale/fr/LC_MESSAGES/oslo_vmware.po0000666000175100017510000001363113224676076025011 0ustar zuulzuul00000000000000# Translations template for oslo.vmware. # Copyright (C) 2015 ORGANIZATION # This file is distributed under the same license as the oslo.vmware # project. # # Translators: # Maxime COQUEREL , 2014-2015 # Andreas Jaeger , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: oslo.vmware 2.15.1.dev2\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2016-10-22 08:58+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2015-07-27 10:54+0000\n" "Last-Translator: Maxime COQUEREL \n" "Language: fr\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "Generated-By: Babel 2.0\n" "X-Generator: Zanata 3.7.3\n" "Language-Team: French\n" msgid "An unknown exception occurred." msgstr "Une exception inconnue s'est produite " msgid "Cannot delete file." msgstr "Le fichier ne peut pas etre supprimé." msgid "Capacity is smaller than free space" msgstr "La capacité est plus petite que l'espace libre" #, python-format msgid "Connection error occurred while writing data to %s." msgstr "Erreur de connexion pendant l'écriture des données de %s" msgid "Could not retrieve VMDK URL from lease info." msgstr "Impossible de récupérer VMDK URL depuis l'emplacement d'information." #, python-format msgid "" "Current session: %(session)s is inactive; re-creating the session while " "invoking method %(module)s.%(method)s." msgstr "" "Session courante: %(session)s est inactive; recréation de la session en " "invoquant la méthode %(module)s.%(method)s." msgid "Datacenter name cannot be None" msgstr "Le nom du datacenter ne peut pas être \"vide\"" msgid "Datacenter reference cannot be None" msgstr "La référence du datastore ne peut pas etre \"vide\"" msgid "Datastore name cannot be None" msgstr "Le nom du datastore ne peut pas etre \"vide\"" msgid "Datastore name cannot be empty" msgstr "Le nom du datastore ne pas etre \"vide\"" msgid "Datastore path cannot be empty" msgstr "Le chemin du datastore ne peut pas etre \"vide\"" msgid "Datastore reference cannot be None" msgstr "La référence du datastore ne peut pas etre \"vide\"" msgid "Duplicate name." msgstr "Duplicat du nom." msgid "Entity has another operation in process." msgstr "L'entité a une autre opération en cours." msgid "Error occurred while calling RetrievePropertiesEx." msgstr "Une erreur est survenue pendant l'appelle RetrievePropertiesEx." #, python-format msgid "" "Error occurred while creating HTTP connection to write to VMDK file with URL " "= %s." msgstr "" "Une erreur est survenue pendant la création des connexion HTTP pour écrire " "le fichier VMDK avec URL = %s." #, python-format msgid "" "Error occurred while creating HTTP connection to write to file with URL = %s." msgstr "" "Une erreur est survenue pendant la création des connexion HTTP pour écrire " "le fichier avec URL = %s." #, python-format msgid "Error occurred while opening URL: %s for reading." msgstr "" "Une erreur est survenue pendant l'ouverture de URL: %s pour la lecture." #, python-format msgid "Error occurred while reading data from %s." msgstr "Une erreur est survenue pendant la lecture des données depuis %s." #, python-format msgid "Error occurred while writing data to %s." msgstr "Une erreur est survenue pendant l'écriture des données de %s." #, python-format msgid "Exception in %s." msgstr "Exception dans %s." msgid "File already exists." msgstr "Le fichier existe déjà." msgid "File fault." msgstr "Redirection fichier." msgid "File locked." msgstr "Fichier verrouillé." msgid "File not found." msgstr "Fichier non trouvé." msgid "Insufficient disk space." msgstr "Espace disque insuffisant." msgid "Invalid capacity" msgstr "Capacité non valide" msgid "Invalid power state." msgstr "Status de démarrage non valide." msgid "Invalid property." msgstr "Propriété non valide." #, python-format msgid "Invalid scheme: %s." msgstr "Invalide schéma: %s" #, python-format msgid "Lease: %(lease)s is in error state. Details: %(error_msg)s." msgstr "Bail: %(lease)s est dans une erreur de status. Détails: %(error_msg)s." msgid "Managed object not found." msgstr "Objet géré introuvable." #, python-format msgid "Missing parameter : %(param)s" msgstr "Paramètre manquant : %(param)s" msgid "No Permission." msgstr "Pas de permission." msgid "No default value for use_linked_clone found." msgstr "Pas de valeur par défaut pour use_linked_clone trouvé." #, python-format msgid "No such SOAP method %s." msgstr "Pas de méthode SOAP %s." msgid "Not Authenticated." msgstr "Non authentifié." msgid "Path component cannot be None" msgstr "Le chemin du composant ne peut pas etre \"vide\"" msgid "Resource already exists." msgstr "La ressource existe déjà." #, python-format msgid "Socket error in %s." msgstr "Erreur de socket dans %s." #, python-format msgid "Type error in %s." msgstr "Erreur de type dans %s." #, python-format msgid "Unknown state: %(state)s for lease: %(lease)s." msgstr "Statut inconnu: %(state)s for lease: %(lease)s." msgid "VMware Driver configuration fault." msgstr "Redirection de la configuration du pilote VMware." msgid "VMware Tools is not running." msgstr "VMware Tools ne fonctionne pas." msgid "cause" msgstr "cause" msgid "datacenter must be set to build url" msgstr "datacenter doit être initialiser pour construire url" msgid "details must be a dict" msgstr "details doit etre un dict" msgid "exception message must not be a list" msgstr "exception ne doit pas etre une liste" msgid "exception should be a subclass of VimException" msgstr "exception devrait être une subclass de VimException" msgid "fault_list must be a list" msgstr "fault_list doit etre une liste" #, python-format msgid "httplib error in %s." msgstr "httplib erreur dans %s." #, python-format msgid "requests error in %s." msgstr "erreur de requetes dans %s." msgid "string" msgstr "chaîne de caractère" oslo.vmware-2.26.0/oslo_vmware/exceptions.py0000666000175100017510000002253513224676076021217 0ustar zuulzuul00000000000000# Copyright (c) 2014 VMware, Inc. # All Rights Reserved. # # 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. """ Exception definitions. """ import logging import six from oslo_vmware._i18n import _ LOG = logging.getLogger(__name__) ALREADY_EXISTS = 'AlreadyExists' CANNOT_DELETE_FILE = 'CannotDeleteFile' DUPLICATE_NAME = 'DuplicateName' FILE_ALREADY_EXISTS = 'FileAlreadyExists' FILE_FAULT = 'FileFault' FILE_LOCKED = 'FileLocked' FILE_NOT_FOUND = 'FileNotFound' INVALID_POWER_STATE = 'InvalidPowerState' INVALID_PROPERTY = 'InvalidProperty' NO_DISK_SPACE = 'NoDiskSpace' NO_PERMISSION = 'NoPermission' NOT_AUTHENTICATED = 'NotAuthenticated' SECURITY_ERROR = "SecurityError" MANAGED_OBJECT_NOT_FOUND = 'ManagedObjectNotFound' TASK_IN_PROGRESS = 'TaskInProgress' TOOLS_UNAVAILABLE = 'ToolsUnavailable' class VMwareDriverException(Exception): """Base oslo.vmware exception To correctly use this class, inherit from it and define a 'msg_fmt' property. That msg_fmt will get printf'd with the keyword arguments provided to the constructor. """ msg_fmt = _("An unknown exception occurred.") if six.PY2: __str__ = lambda self: six.text_type(self).encode('utf8') __unicode__ = lambda self: self.description else: __str__ = lambda self: self.description def __init__(self, message=None, details=None, **kwargs): if message is not None and isinstance(message, list): # we need this to protect against developers using # this method like VimFaultException raise ValueError(_("exception message must not be a list")) if details is not None and not isinstance(details, dict): raise ValueError(_("details must be a dict")) self.kwargs = kwargs self.details = details self.cause = None if not message: try: message = self.msg_fmt % kwargs except Exception: # kwargs doesn't match a variable in the message # log the issue and the kwargs LOG.exception('Exception in string format operation') for name, value in six.iteritems(kwargs): LOG.error("%(name)s: %(value)s", {'name': name, 'value': value}) # at least get the core message out if something happened message = self.msg_fmt self.message = message super(VMwareDriverException, self).__init__(message) @property def msg(self): return self.message @property def description(self): # NOTE(jecarey): self.msg and self.cause may be i18n objects # that do not support str or concatenation, but can be used # as replacement text. descr = six.text_type(self.msg) if self.cause: descr += '\nCause: ' + six.text_type(self.cause) return descr class VimException(VMwareDriverException): """The base exception class for all VIM related exceptions.""" def __init__(self, message=None, cause=None, details=None, **kwargs): super(VimException, self).__init__(message, details, **kwargs) self.cause = cause class VimSessionOverLoadException(VMwareDriverException): """Thrown when there is an API call overload at the VMware server.""" def __init__(self, message, cause=None): super(VimSessionOverLoadException, self).__init__(message) self.cause = cause class VimConnectionException(VMwareDriverException): """Thrown when there is a connection problem.""" def __init__(self, message, cause=None): super(VimConnectionException, self).__init__(message) self.cause = cause class VimAttributeException(VMwareDriverException): """Thrown when a particular attribute cannot be found.""" def __init__(self, message, cause=None): super(VimAttributeException, self).__init__(message) self.cause = cause class VimFaultException(VimException): """Exception thrown when there are unrecognized VIM faults.""" def __init__(self, fault_list, message, cause=None, details=None): super(VimFaultException, self).__init__(message, cause, details) if not isinstance(fault_list, list): raise ValueError(_("fault_list must be a list")) self.fault_list = fault_list @property def description(self): descr = VimException.description.fget(self) if self.fault_list: # fault_list doesn't contain non-ASCII chars, we can use str() descr += '\nFaults: ' + str(self.fault_list) if self.details: # details may contain non-ASCII values details = '{%s}' % ', '.join(["'%s': '%s'" % (k, v) for k, v in six.iteritems(self.details)]) descr += '\nDetails: ' + details return descr class ImageTransferException(VMwareDriverException): """Thrown when there is an error during image transfer.""" def __init__(self, message, cause=None): super(ImageTransferException, self).__init__(message) self.cause = cause def _print_deprecation_warning(clazz): LOG.warning("Exception %s is deprecated, it will be removed in the " "next release.", clazz.__name__) class VMwareDriverConfigurationException(VMwareDriverException): """Base class for all configuration exceptions. """ msg_fmt = _("VMware Driver configuration fault.") def __init__(self, message=None, details=None, **kwargs): super(VMwareDriverConfigurationException, self).__init__( message, details, **kwargs) _print_deprecation_warning(self.__class__) class UseLinkedCloneConfigurationFault(VMwareDriverConfigurationException): msg_fmt = _("No default value for use_linked_clone found.") class MissingParameter(VMwareDriverException): msg_fmt = _("Missing parameter : %(param)s") def __init__(self, message=None, details=None, **kwargs): super(MissingParameter, self).__init__(message, details, **kwargs) _print_deprecation_warning(self.__class__) class AlreadyExistsException(VimException): msg_fmt = _("Resource already exists.") code = 409 class CannotDeleteFileException(VimException): msg_fmt = _("Cannot delete file.") code = 403 class FileAlreadyExistsException(VimException): msg_fmt = _("File already exists.") code = 409 class FileFaultException(VimException): msg_fmt = _("File fault.") code = 409 class FileLockedException(VimException): msg_fmt = _("File locked.") code = 403 class FileNotFoundException(VimException): msg_fmt = _("File not found.") code = 404 class InvalidPowerStateException(VimException): msg_fmt = _("Invalid power state.") code = 409 class InvalidPropertyException(VimException): msg_fmt = _("Invalid property.") code = 400 class NoPermissionException(VimException): msg_fmt = _("No Permission.") code = 403 class NotAuthenticatedException(VimException): msg_fmt = _("Not Authenticated.") code = 403 class TaskInProgress(VimException): msg_fmt = _("Entity has another operation in process.") class DuplicateName(VimException): msg_fmt = _("Duplicate name.") class NoDiskSpaceException(VimException): msg_fmt = _("Insufficient disk space.") class ToolsUnavailableException(VimException): msg_fmt = _("VMware Tools is not running.") class ManagedObjectNotFoundException(VimException): msg_fmt = _("Managed object not found.") code = 404 # Populate the fault registry with the exceptions that have # special treatment. _fault_classes_registry = { ALREADY_EXISTS: AlreadyExistsException, CANNOT_DELETE_FILE: CannotDeleteFileException, DUPLICATE_NAME: DuplicateName, FILE_ALREADY_EXISTS: FileAlreadyExistsException, FILE_FAULT: FileFaultException, FILE_LOCKED: FileLockedException, FILE_NOT_FOUND: FileNotFoundException, INVALID_POWER_STATE: InvalidPowerStateException, INVALID_PROPERTY: InvalidPropertyException, MANAGED_OBJECT_NOT_FOUND: ManagedObjectNotFoundException, NO_DISK_SPACE: NoDiskSpaceException, NO_PERMISSION: NoPermissionException, NOT_AUTHENTICATED: NotAuthenticatedException, TASK_IN_PROGRESS: TaskInProgress, TOOLS_UNAVAILABLE: ToolsUnavailableException, } def get_fault_class(name): """Get a named subclass of VimException.""" name = str(name) fault_class = _fault_classes_registry.get(name) if not fault_class: LOG.debug('Fault %s not matched.', name) return fault_class def register_fault_class(name, exception): fault_class = _fault_classes_registry.get(name) if not issubclass(exception, VimException): raise TypeError(_("exception should be a subclass of " "VimException")) if fault_class: LOG.debug('Overriding exception for %s', name) _fault_classes_registry[name] = exception oslo.vmware-2.26.0/oslo_vmware/_i18n.py0000666000175100017510000000152413224676076017747 0ustar zuulzuul00000000000000# 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. """oslo.i18n integration module. See https://docs.openstack.org/oslo.i18n/latest/user/index.html """ import oslo_i18n _translators = oslo_i18n.TranslatorFactory(domain='oslo_vmware') # The primary translation function using the well-known name "_" _ = _translators.primary oslo.vmware-2.26.0/oslo_vmware/__init__.py0000666000175100017510000000000013224676076020554 0ustar zuulzuul00000000000000oslo.vmware-2.26.0/oslo_vmware/image_transfer.py0000666000175100017510000003435713224676076022031 0ustar zuulzuul00000000000000# Copyright (c) 2014 VMware, Inc. # All Rights Reserved. # # 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. """ Functions and classes for image transfer between ESX/VC & image service. """ import logging import tarfile from eventlet import timeout import six from oslo_utils import units from oslo_vmware._i18n import _ from oslo_vmware.common import loopingcall from oslo_vmware import constants from oslo_vmware import exceptions from oslo_vmware import image_util from oslo_vmware.objects import datastore as ds_obj from oslo_vmware import rw_handles from oslo_vmware import vim_util LOG = logging.getLogger(__name__) NFC_LEASE_UPDATE_PERIOD = 60 # update NFC lease every 60sec. CHUNK_SIZE = 64 * units.Ki # default chunk size for image transfer def _start_transfer(read_handle, write_handle, timeout_secs): # write_handle could be an NFC lease, so we need to periodically # update its progress update_cb = getattr(write_handle, 'update_progress', lambda: None) updater = loopingcall.FixedIntervalLoopingCall(update_cb) timer = timeout.Timeout(timeout_secs) try: updater.start(interval=NFC_LEASE_UPDATE_PERIOD) while True: data = read_handle.read(CHUNK_SIZE) if not data: break write_handle.write(data) except timeout.Timeout as excep: msg = (_('Timeout, read_handle: "%(src)s", write_handle: "%(dest)s"') % {'src': read_handle, 'dest': write_handle}) LOG.exception(msg) raise exceptions.ImageTransferException(msg, excep) except Exception as excep: msg = (_('Error, read_handle: "%(src)s", write_handle: "%(dest)s"') % {'src': read_handle, 'dest': write_handle}) LOG.exception(msg) raise exceptions.ImageTransferException(msg, excep) finally: timer.cancel() updater.stop() read_handle.close() write_handle.close() def download_image(image, image_meta, session, datastore, rel_path, bypass=True, timeout_secs=7200): """Transfer an image to a datastore. :param image: file-like iterator :param image_meta: image metadata :param session: VMwareAPISession object :param datastore: Datastore object :param rel_path: path where the file will be stored in the datastore :param bypass: if set to True, bypass vCenter to download the image :param timeout_secs: time in seconds to wait for the xfer to complete """ image_size = int(image_meta['size']) method = 'PUT' if bypass: hosts = datastore.get_connected_hosts(session) host = ds_obj.Datastore.choose_host(hosts) host_name = session.invoke_api(vim_util, 'get_object_property', session.vim, host, 'name') ds_url = datastore.build_url(session._scheme, host_name, rel_path, constants.ESX_DATACENTER_PATH) cookie = ds_url.get_transfer_ticket(session, method) conn = ds_url.connect(method, image_size, cookie) else: ds_url = datastore.build_url(session._scheme, session._host, rel_path) cookie = '%s=%s' % (constants.SOAP_COOKIE_KEY, session.vim.get_http_cookie().strip("\"")) conn = ds_url.connect(method, image_size, cookie) conn.write = conn.send read_handle = rw_handles.ImageReadHandle(image) _start_transfer(read_handle, conn, timeout_secs) def download_flat_image(context, timeout_secs, image_service, image_id, **kwargs): """Download flat image from the image service to VMware server. :param context: image service write context :param timeout_secs: time in seconds to wait for the download to complete :param image_service: image service handle :param image_id: ID of the image to be downloaded :param kwargs: keyword arguments to configure the destination file write handle :raises: VimConnectionException, ImageTransferException, ValueError """ LOG.debug("Downloading image: %s from image service as a flat file.", image_id) # TODO(vbala) catch specific exceptions raised by download call read_iter = image_service.download(context, image_id) read_handle = rw_handles.ImageReadHandle(read_iter) file_size = int(kwargs.get('image_size')) write_handle = rw_handles.FileWriteHandle(kwargs.get('host'), kwargs.get('port'), kwargs.get('data_center_name'), kwargs.get('datastore_name'), kwargs.get('cookies'), kwargs.get('file_path'), file_size, cacerts=kwargs.get('cacerts')) _start_transfer(read_handle, write_handle, timeout_secs) LOG.debug("Downloaded image: %s from image service as a flat file.", image_id) def download_file( read_handle, host, port, dc_name, ds_name, cookies, upload_file_path, file_size, cacerts, timeout_secs): """Download file to VMware server. :param read_handle: file read handle :param host: VMware server host name or IP address :param port: VMware server port number :param dc_name: name of the datacenter which contains the destination datastore :param ds_name: name of the destination datastore :param cookies: cookies to build the cookie header while establishing http connection with VMware server :param upload_file_path: destination datastore file path :param file_size: source file size :param cacerts: CA bundle file to use for SSL verification :param timeout_secs: timeout in seconds to wait for the download to complete """ write_handle = rw_handles.FileWriteHandle(host, port, dc_name, ds_name, cookies, upload_file_path, file_size, cacerts=cacerts) _start_transfer(read_handle, write_handle, timeout_secs) def download_stream_optimized_data(context, timeout_secs, read_handle, **kwargs): """Download stream optimized data to VMware server. :param context: image service write context :param timeout_secs: time in seconds to wait for the download to complete :param read_handle: handle from which to read the image data :param kwargs: keyword arguments to configure the destination VMDK write handle :returns: managed object reference of the VM created for import to VMware server :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException, VimConnectionException, ImageTransferException, ValueError """ file_size = int(kwargs.get('image_size')) write_handle = rw_handles.VmdkWriteHandle(kwargs.get('session'), kwargs.get('host'), kwargs.get('port'), kwargs.get('resource_pool'), kwargs.get('vm_folder'), kwargs.get('vm_import_spec'), file_size, kwargs.get('http_method', 'PUT')) _start_transfer(read_handle, write_handle, timeout_secs) return write_handle.get_imported_vm() def _get_vmdk_handle(ova_handle): with tarfile.open(mode="r|", fileobj=ova_handle) as tar: vmdk_name = None for tar_info in tar: if tar_info and tar_info.name.endswith(".ovf"): vmdk_name = image_util.get_vmdk_name_from_ovf( tar.extractfile(tar_info)) elif vmdk_name and tar_info.name.startswith(vmdk_name): # Actual file name is .XXXXXXX return tar.extractfile(tar_info) def download_stream_optimized_image(context, timeout_secs, image_service, image_id, **kwargs): """Download stream optimized image from image service to VMware server. :param context: image service write context :param timeout_secs: time in seconds to wait for the download to complete :param image_service: image service handle :param image_id: ID of the image to be downloaded :param kwargs: keyword arguments to configure the destination VMDK write handle :returns: managed object reference of the VM created for import to VMware server :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException, VimConnectionException, ImageTransferException, ValueError """ metadata = image_service.show(context, image_id) container_format = metadata.get('container_format') LOG.debug("Downloading image: %(id)s (container: %(container)s) from image" " service as a stream optimized file.", {'id': image_id, 'container': container_format}) # TODO(vbala) catch specific exceptions raised by download call read_iter = image_service.download(context, image_id) read_handle = rw_handles.ImageReadHandle(read_iter) if container_format == 'ova': read_handle = _get_vmdk_handle(read_handle) if read_handle is None: raise exceptions.ImageTransferException( _("No vmdk found in the OVA image %s.") % image_id) imported_vm = download_stream_optimized_data(context, timeout_secs, read_handle, **kwargs) LOG.debug("Downloaded image: %s from image service as a stream " "optimized file.", image_id) return imported_vm def copy_stream_optimized_disk( context, timeout_secs, write_handle, **kwargs): """Copy virtual disk from VMware server to the given write handle. :param context: context :param timeout_secs: time in seconds to wait for the copy to complete :param write_handle: copy destination :param kwargs: keyword arguments to configure the source VMDK read handle :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException, VimConnectionException, ImageTransferException, ValueError """ vmdk_file_path = kwargs.get('vmdk_file_path') LOG.debug("Copying virtual disk: %(vmdk_path)s to %(dest)s.", {'vmdk_path': vmdk_file_path, 'dest': write_handle.name}) file_size = kwargs.get('vmdk_size') read_handle = rw_handles.VmdkReadHandle(kwargs.get('session'), kwargs.get('host'), kwargs.get('port'), kwargs.get('vm'), kwargs.get('vmdk_file_path'), file_size) _start_transfer(read_handle, write_handle, timeout_secs) LOG.debug("Downloaded virtual disk: %s.", vmdk_file_path) # TODO(vbala) Remove dependency on image service provided by the client. def upload_image(context, timeout_secs, image_service, image_id, owner_id, **kwargs): """Upload the VM's disk file to image service. :param context: image service write context :param timeout_secs: time in seconds to wait for the upload to complete :param image_service: image service handle :param image_id: upload destination image ID :param kwargs: keyword arguments to configure the source VMDK read handle :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException, VimConnectionException, ImageTransferException, ValueError """ LOG.debug("Uploading to image: %s.", image_id) file_size = kwargs.get('vmdk_size') read_handle = rw_handles.VmdkReadHandle(kwargs.get('session'), kwargs.get('host'), kwargs.get('port'), kwargs.get('vm'), kwargs.get('vmdk_file_path'), file_size) # TODO(vbala) Remove this after we delete the keyword argument 'is_public' # from all client code. if 'is_public' in kwargs: LOG.debug("Ignoring keyword argument 'is_public'.") if 'image_version' in kwargs: LOG.warning("The keyword argument 'image_version' is deprecated " "and will be ignored in the next release.") image_ver = six.text_type(kwargs.get('image_version')) image_metadata = {'disk_format': 'vmdk', 'name': kwargs.get('image_name'), 'properties': {'vmware_image_version': image_ver, 'vmware_disktype': 'streamOptimized', 'owner_id': owner_id}} updater = loopingcall.FixedIntervalLoopingCall(read_handle.update_progress) try: updater.start(interval=NFC_LEASE_UPDATE_PERIOD) image_service.update(context, image_id, image_metadata, data=read_handle) finally: updater.stop() read_handle.close() LOG.debug("Uploaded image: %s.", image_id) oslo.vmware-2.26.0/oslo_vmware/image_util.py0000666000175100017510000000234113224676076021146 0ustar zuulzuul00000000000000# Copyright (c) 2016 VMware, Inc. # All Rights Reserved. # # 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 lxml import etree # nosec (bandit bug 1582516) def _get_vmdk_name_from_ovf(root): ns_ovf = "{{{0}}}".format(root.nsmap["ovf"]) disk = root.find("./{0}DiskSection/{0}Disk".format(ns_ovf)) file_id = disk.get("{0}fileRef".format(ns_ovf)) f = root.find('./{0}References/{0}File[@{0}id="{1}"]'.format(ns_ovf, file_id)) return f.get("{0}href".format(ns_ovf)) def get_vmdk_name_from_ovf(ovf_handle): """Get the vmdk name from the given ovf descriptor.""" return _get_vmdk_name_from_ovf(etree.parse(ovf_handle).getroot()) oslo.vmware-2.26.0/oslo_vmware/pbm.py0000666000175100017510000002111013224676076017600 0ustar zuulzuul00000000000000# Copyright (c) 2014 VMware, Inc. # All Rights Reserved. # # 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. """ VMware PBM service client and PBM related utility methods PBM is used for policy based placement in VMware datastores. Refer http://goo.gl/GR2o6U for more details. """ import logging import os import six.moves.urllib.parse as urlparse import six.moves.urllib.request as urllib from oslo_vmware import service from oslo_vmware import vim_util SERVICE_TYPE = 'PbmServiceInstance' LOG = logging.getLogger(__name__) 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, cacert=None, insecure=True, pool_maxsize=10, connection_timeout=None, op_id_prefix='oslo.vmware'): """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 :param pool_maxsize: Maximum number of connections in http connection pool :param op_id_prefix: String prefix for the operation ID. :param connection_timeout: Maximum time in seconds to wait for peer to respond. """ base_url = service.Service.build_base_url(protocol, host, port) soap_url = base_url + '/pbm' super(Pbm, self).__init__(wsdl_url, soap_url, cacert, insecure, pool_maxsize, connection_timeout, op_id_prefix) def set_soap_cookie(self, cookie): """Set the specified vCenter session cookie in the SOAP header :param cookie: cookie to set """ self._vc_session_cookie = cookie def retrieve_service_content(self): ref = vim_util.get_moref(service.SERVICE_INSTANCE, SERVICE_TYPE) return self.PbmRetrieveServiceContent(ref) def __repr__(self): return "PBM Object" def __str__(self): return "PBM Object" def get_all_profiles(session): """Get all the profiles defined in VC server. :returns: PbmProfile data objects :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException, VimConnectionException """ LOG.debug("Fetching all the profiles defined in VC server.") pbm = session.pbm profile_manager = pbm.service_content.profileManager res_type = pbm.client.factory.create('ns0:PbmProfileResourceType') res_type.resourceType = 'STORAGE' profiles = [] profile_ids = session.invoke_api(pbm, 'PbmQueryProfile', profile_manager, resourceType=res_type) LOG.debug("Fetched profile IDs: %s.", profile_ids) if profile_ids: profiles = session.invoke_api(pbm, 'PbmRetrieveContent', profile_manager, profileIds=profile_ids) return profiles def get_profile_id_by_name(session, profile_name): """Get the profile UUID corresponding to the given profile name. :param profile_name: profile name whose UUID needs to be retrieved :returns: profile UUID string or None if profile not found :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException, VimConnectionException """ LOG.debug("Retrieving profile ID for profile: %s.", profile_name) for profile in get_all_profiles(session): if profile.name == profile_name: profile_id = profile.profileId LOG.debug("Retrieved profile ID: %(id)s for profile: %(name)s.", {'id': profile_id, 'name': profile_name}) return profile_id return None def filter_hubs_by_profile(session, hubs, profile_id): """Filter and return hubs that match the given profile. :param hubs: PbmPlacementHub morefs :param profile_id: profile ID :returns: subset of hubs that match the given profile :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException, VimConnectionException """ LOG.debug("Filtering hubs: %(hubs)s that match profile: %(profile)s.", {'hubs': hubs, 'profile': profile_id}) pbm = session.pbm placement_solver = pbm.service_content.placementSolver filtered_hubs = session.invoke_api(pbm, 'PbmQueryMatchingHub', placement_solver, hubsToSearch=hubs, profile=profile_id) LOG.debug("Filtered hubs: %s", filtered_hubs) return filtered_hubs def convert_datastores_to_hubs(pbm_client_factory, datastores): """Convert given datastore morefs to PbmPlacementHub morefs. :param pbm_client_factory: Factory to create PBM API input specs :param datastores: list of datastore morefs :returns: list of PbmPlacementHub morefs """ hubs = [] for ds in datastores: hub = pbm_client_factory.create('ns0:PbmPlacementHub') hub.hubId = ds.value hub.hubType = 'Datastore' hubs.append(hub) return hubs def filter_datastores_by_hubs(hubs, datastores): """Get filtered subset of datastores corresponding to the given hub list. :param hubs: list of PbmPlacementHub morefs :param datastores: all candidate datastores :returns: subset of datastores corresponding to the given hub list """ filtered_dss = [] hub_ids = [hub.hubId for hub in hubs] for ds in datastores: if ds.value in hub_ids: filtered_dss.append(ds) return filtered_dss def get_pbm_wsdl_location(vc_version): """Return PBM WSDL file location corresponding to VC version. :param vc_version: a dot-separated version string. For example, "1.2". :return: the pbm wsdl file location. """ if not vc_version: return ver = vc_version.split('.') major_minor = ver[0] if len(ver) >= 2: major_minor = '%s.%s' % (major_minor, ver[1]) curr_dir = os.path.abspath(os.path.dirname(__file__)) pbm_service_wsdl = os.path.join(curr_dir, 'wsdl', major_minor, 'pbmService.wsdl') if not os.path.exists(pbm_service_wsdl): LOG.warning("PBM WSDL file %s not found.", pbm_service_wsdl) return pbm_wsdl = urlparse.urljoin('file:', urllib.pathname2url(pbm_service_wsdl)) LOG.debug("Using PBM WSDL location: %s.", pbm_wsdl) return pbm_wsdl def get_profiles(session, vm): """Query storage profiles associated with the given vm. :param session: VMwareAPISession instance :param vm: vm reference :return: profile IDs """ pbm = session.pbm profile_manager = pbm.service_content.profileManager object_ref = pbm.client.factory.create('ns0:PbmServerObjectRef') object_ref.key = vm.value object_ref.objectType = 'virtualMachine' return session.invoke_api(pbm, 'PbmQueryAssociatedProfile', profile_manager, entity=object_ref) def get_profiles_by_ids(session, profile_ids): """Get storage profiles by IDs. :param session: VMwareAPISession instance :param profile_ids: profile IDs :return: profile objects """ profiles = [] if profile_ids: pbm = session.pbm profile_manager = pbm.service_content.profileManager profiles = session.invoke_api(pbm, 'PbmRetrieveContent', profile_manager, profileIds=profile_ids) return profiles oslo.vmware-2.26.0/oslo_vmware/hacking/0000775000175100017510000000000013224676314020052 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/oslo_vmware/hacking/checks.py0000666000175100017510000000330613224676076021675 0ustar zuulzuul00000000000000# Copyright (c) 2017 OpenStack Foundation. # # 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. import re _all_log_levels = {'critical', 'error', 'exception', 'info', 'warning', 'debug'} # Since _Lx() have been removed, we just need to check _() _all_hints = {'_'} _log_translation_hint = re.compile( r".*LOG\.(%(levels)s)\(\s*(%(hints)s)\(" % { 'levels': '|'.join(_all_log_levels), 'hints': '|'.join(_all_hints), }) def no_translate_logs(logical_line, filename): """N537 - Don't translate logs. Check for 'LOG.*(_(' Translators don't provide translations for log messages, and operators asked not to translate them. * This check assumes that 'LOG' is a logger. :param logical_line: The logical line to check. :param filename: The file name where the logical line exists. :returns: None if the logical line passes the check, otherwise a tuple is yielded that contains the offending index in logical line and a message describe the check validation failure. """ if _log_translation_hint.match(logical_line): yield (0, "N537: Log messages should not be translated!") def factory(register): register(no_translate_logs) oslo.vmware-2.26.0/oslo_vmware/hacking/__init__.py0000666000175100017510000000000013224676076022160 0ustar zuulzuul00000000000000oslo.vmware-2.26.0/oslo_vmware/common/0000775000175100017510000000000013224676314017736 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/oslo_vmware/common/__init__.py0000666000175100017510000000000013224676076022044 0ustar zuulzuul00000000000000oslo.vmware-2.26.0/oslo_vmware/common/loopingcall.py0000666000175100017510000001061513224676076022625 0ustar zuulzuul00000000000000# Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # Copyright 2011 Justin Santa Barbara # All Rights Reserved. # # 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. import logging import sys from eventlet import event from eventlet import greenthread from oslo_utils import timeutils LOG = logging.getLogger(__name__) class LoopingCallDone(Exception): """Exception to break out and stop a LoopingCall. The poll-function passed to LoopingCall can raise this exception to break out of the loop normally. This is somewhat analogous to StopIteration. An optional return-value can be included as the argument to the exception; this return-value will be returned by LoopingCall.wait() """ def __init__(self, retvalue=True): """:param retvalue: Value that LoopingCall.wait() should return.""" self.retvalue = retvalue class LoopingCallBase(object): def __init__(self, f=None, *args, **kw): self.args = args self.kw = kw self.f = f self._running = False self.done = None def stop(self): self._running = False def wait(self): return self.done.wait() class FixedIntervalLoopingCall(LoopingCallBase): """A fixed interval looping call.""" def start(self, interval, initial_delay=None): self._running = True done = event.Event() def _inner(): if initial_delay: greenthread.sleep(initial_delay) try: while self._running: start = timeutils.utcnow() self.f(*self.args, **self.kw) end = timeutils.utcnow() if not self._running: break delay = interval - timeutils.delta_seconds(start, end) if delay <= 0: LOG.warning('task run outlasted interval ' 'by %s sec', -delay) greenthread.sleep(delay if delay > 0 else 0) except LoopingCallDone as e: self.stop() done.send(e.retvalue) except Exception: LOG.exception('in fixed duration looping call') done.send_exception(*sys.exc_info()) return else: done.send(True) self.done = done greenthread.spawn_n(_inner) return self.done # TODO(mikal): this class name is deprecated in Havana and should be removed # in the I release LoopingCall = FixedIntervalLoopingCall class DynamicLoopingCall(LoopingCallBase): """A looping call which sleeps until the next known event. The function called should return how long to sleep for before being called again. """ def start(self, initial_delay=None, periodic_interval_max=None): self._running = True done = event.Event() def _inner(): if initial_delay: greenthread.sleep(initial_delay) try: while self._running: idle = self.f(*self.args, **self.kw) if not self._running: break if periodic_interval_max is not None: idle = min(idle, periodic_interval_max) LOG.debug('Dynamic looping call sleeping for %.02f ' 'seconds', idle) greenthread.sleep(idle) except LoopingCallDone as e: self.stop() done.send(e.retvalue) except Exception: done.send_exception(*sys.exc_info()) return else: done.send(True) self.done = done greenthread.spawn(_inner) return self.done oslo.vmware-2.26.0/oslo_vmware/rw_handles.py0000666000175100017510000006173513224676076021171 0ustar zuulzuul00000000000000# Copyright (c) 2014 VMware, Inc. # All Rights Reserved. # # 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. """ Classes defining read and write handles for image transfer. This module defines various classes for reading and writing files including VMDK files in VMware servers. It also contains a class to read images from glance server. """ import logging import ssl import time from oslo_utils import excutils from oslo_utils import netutils import requests import six import six.moves.urllib.parse as urlparse from urllib3 import connection as httplib from oslo_vmware._i18n import _ from oslo_vmware import exceptions from oslo_vmware import vim_util LOG = logging.getLogger(__name__) MIN_PROGRESS_DIFF_TO_LOG = 25 MIN_UPDATE_INTERVAL = 60 READ_CHUNKSIZE = 65536 USER_AGENT = 'OpenStack-ESX-Adapter' class FileHandle(object): """Base class for VMware server file (including VMDK) access over HTTP. This class wraps a backing file handle and provides utility methods for various sub-classes. """ def __init__(self, file_handle): """Initializes the file handle. :param file_handle: backing file handle """ self._eof = False self._file_handle = file_handle def _create_connection(self, url, method, cacerts=False, ssl_thumbprint=None): _urlparse = urlparse.urlparse(url) scheme, netloc, path, params, query, fragment = _urlparse if scheme == 'http': conn = httplib.HTTPConnection(netloc) elif scheme == 'https': conn = httplib.HTTPSConnection(netloc) cert_reqs = None # cacerts can be either True or False or contain # actual certificates. If it is a boolean, then # we need to set cert_reqs and clear the cacerts if isinstance(cacerts, bool): if cacerts: cert_reqs = ssl.CERT_REQUIRED else: cert_reqs = ssl.CERT_NONE cacerts = None conn.set_cert(ca_certs=cacerts, cert_reqs=cert_reqs, assert_fingerprint=ssl_thumbprint) else: excep_msg = _("Invalid scheme: %s.") % scheme LOG.error(excep_msg) raise ValueError(excep_msg) if query: path = path + '?' + query conn.putrequest(method, path) return conn def _create_read_connection(self, url, cookies=None, cacerts=False, ssl_thumbprint=None): LOG.debug("Opening URL: %s for reading.", url) try: conn = self._create_connection(url, 'GET', cacerts, ssl_thumbprint) vim_cookie = self._build_vim_cookie_header(cookies) conn.putheader('User-Agent', USER_AGENT) conn.putheader('Cookie', vim_cookie) conn.endheaders() return conn.getresponse() except Exception as excep: # TODO(vbala) We need to catch and raise specific exceptions # related to connection problems, invalid request and invalid # arguments. excep_msg = _("Error occurred while opening URL: %s for " "reading.") % url LOG.exception(excep_msg) raise exceptions.VimException(excep_msg, excep) def _create_write_connection(self, method, url, file_size=None, cookies=None, overwrite=None, content_type=None, cacerts=False, ssl_thumbprint=None): """Create HTTP connection to write to VMDK file.""" LOG.debug("Creating HTTP connection to write to file with " "size = %(file_size)d and URL = %(url)s.", {'file_size': file_size, 'url': url}) try: conn = self._create_connection(url, method, cacerts, ssl_thumbprint) headers = {'User-Agent': USER_AGENT} if file_size: headers.update({'Content-Length': str(file_size)}) if overwrite: headers.update({'Overwrite': overwrite}) if cookies: headers.update({'Cookie': self._build_vim_cookie_header(cookies)}) if content_type: headers.update({'Content-Type': content_type}) for key, value in six.iteritems(headers): conn.putheader(key, value) conn.endheaders() return conn except requests.RequestException as excep: excep_msg = _("Error occurred while creating HTTP connection " "to write to VMDK file with URL = %s.") % url LOG.exception(excep_msg) raise exceptions.VimConnectionException(excep_msg, excep) def close(self): """Close the file handle.""" try: self._file_handle.close() except Exception: LOG.warning("Error occurred while closing the file handle", exc_info=True) def _build_vim_cookie_header(self, vim_cookies): """Build ESX host session cookie header.""" cookie_header = "" for vim_cookie in vim_cookies: cookie_header = vim_cookie.name + '=' + vim_cookie.value break return cookie_header def write(self, data): """Write data to the file. :param data: data to be written :raises: NotImplementedError """ raise NotImplementedError() def read(self, chunk_size): """Read a chunk of data. :param chunk_size: read chunk size :raises: NotImplementedError """ raise NotImplementedError() def get_size(self): """Get size of the file to be read. :raises: NotImplementedError """ raise NotImplementedError() def _get_soap_url(self, scheme, host, port): """Returns the IPv4/v6 compatible SOAP URL for the given host.""" if netutils.is_valid_ipv6(host): return '%s://[%s]:%d' % (scheme, host, port) return '%s://%s:%d' % (scheme, host, port) class FileWriteHandle(FileHandle): """Write handle for a file in VMware server.""" def __init__(self, host, port, data_center_name, datastore_name, cookies, file_path, file_size, scheme='https', cacerts=False, thumbprint=None): """Initializes the write handle with given parameters. :param host: ESX/VC server IP address or host name :param port: port for connection :param data_center_name: name of the data center in the case of a VC server :param datastore_name: name of the datastore where the file is stored :param cookies: cookies to build the vim cookie header :param file_path: datastore path where the file is written :param file_size: size of the file in bytes :param scheme: protocol-- http or https :param cacerts: CA bundle file to use for SSL verification :param thumbprint: expected SHA1 thumbprint of server's certificate :raises: VimConnectionException, ValueError """ soap_url = self._get_soap_url(scheme, host, port) param_list = {'dcPath': data_center_name, 'dsName': datastore_name} self._url = '%s/folder/%s' % (soap_url, file_path) self._url = self._url + '?' + urlparse.urlencode(param_list) self._conn = self._create_write_connection('PUT', self._url, file_size, cookies=cookies, cacerts=cacerts, ssl_thumbprint=thumbprint) FileHandle.__init__(self, self._conn) def write(self, data): """Write data to the file. :param data: data to be written :raises: VimConnectionException, VimException """ try: self._file_handle.send(data) except requests.RequestException as excep: excep_msg = _("Connection error occurred while writing data to" " %s.") % self._url LOG.exception(excep_msg) raise exceptions.VimConnectionException(excep_msg, excep) except Exception as excep: # TODO(vbala) We need to catch and raise specific exceptions # related to connection problems, invalid request and invalid # arguments. excep_msg = _("Error occurred while writing data to" " %s.") % self._url LOG.exception(excep_msg) raise exceptions.VimException(excep_msg, excep) def close(self): """Get the response and close the connection.""" LOG.debug("Closing write handle for %s.", self._url) try: self._conn.getresponse() except Exception: LOG.warning("Error occurred while reading the HTTP response.", exc_info=True) super(FileWriteHandle, self).close() def __str__(self): return "File write handle for %s" % self._url class VmdkHandle(FileHandle): """VMDK handle based on HttpNfcLease.""" def __init__(self, session, lease, url, file_handle): self._session = session self._lease = lease self._url = url self._last_logged_progress = 0 self._last_progress_udpate = 0 super(VmdkHandle, self).__init__(file_handle) def _log_progress(self, progress): """Log data transfer progress.""" if (progress == 100 or (progress - self._last_logged_progress >= MIN_PROGRESS_DIFF_TO_LOG)): LOG.debug("Data transfer progress is %d%%.", progress) self._last_logged_progress = progress def _get_progress(self): """Get current progress for updating progress to lease.""" pass def update_progress(self): """Updates progress to lease. This call back to the lease is essential to keep the lease alive across long running write/read operations. :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException, VimConnectionException """ now = time.time() if (now - self._last_progress_udpate < MIN_UPDATE_INTERVAL): return self._last_progress_udpate = now progress = int(self._get_progress()) self._log_progress(progress) try: self._session.invoke_api(self._session.vim, 'HttpNfcLeaseProgress', self._lease, percent=progress) except exceptions.VimException: with excutils.save_and_reraise_exception(): LOG.exception("Error occurred while updating the " "write/read progress of VMDK file " "with URL = %s.", self._url) def _release_lease(self): """Release the lease :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException, VimConnectionException """ LOG.debug("Getting lease state for %s.", self._url) state = self._session.invoke_api(vim_util, 'get_object_property', self._session.vim, self._lease, 'state') LOG.debug("Lease for %(url)s is in state: %(state)s.", {'url': self._url, 'state': state}) if state == 'ready': LOG.debug("Releasing lease for %s.", self._url) self._session.invoke_api(self._session.vim, 'HttpNfcLeaseComplete', self._lease) else: LOG.debug("Lease for %(url)s is in state: %(state)s; no " "need to release.", {'url': self._url, 'state': state}) @staticmethod def _create_import_vapp_lease(session, rp_ref, import_spec, vm_folder_ref): """Create and wait for HttpNfcLease lease for vApp import.""" LOG.debug("Creating HttpNfcLease lease for vApp import into resource" " pool: %s.", rp_ref) lease = session.invoke_api(session.vim, 'ImportVApp', rp_ref, spec=import_spec, folder=vm_folder_ref) LOG.debug("Lease: %(lease)s obtained for vApp import into resource" " pool %(rp_ref)s.", {'lease': lease, 'rp_ref': rp_ref}) session.wait_for_lease_ready(lease) LOG.debug("Invoking VIM API for reading info of lease: %s.", lease) lease_info = session.invoke_api(vim_util, 'get_object_property', session.vim, lease, 'info') return lease, lease_info @staticmethod def _create_export_vm_lease(session, vm_ref): """Create and wait for HttpNfcLease lease for VM export.""" LOG.debug("Creating HttpNfcLease lease for exporting VM: %s.", vm_ref) lease = session.invoke_api(session.vim, 'ExportVm', vm_ref) LOG.debug("Lease: %(lease)s obtained for exporting VM: %(vm_ref)s.", {'lease': lease, 'vm_ref': vm_ref}) session.wait_for_lease_ready(lease) LOG.debug("Invoking VIM API for reading info of lease: %s.", lease) lease_info = session.invoke_api(vim_util, 'get_object_property', session.vim, lease, 'info') return lease, lease_info @staticmethod def _fix_esx_url(url, host, port): """Fix netloc in the case of an ESX host. In the case of an ESX host, the netloc is set to '*' in the URL returned in HttpNfcLeaseInfo. It should be replaced with host name or IP address. """ urlp = urlparse.urlparse(url) if urlp.netloc == '*': scheme, netloc, path, params, query, fragment = urlp if netutils.is_valid_ipv6(host): netloc = '[%s]:%d' % (host, port) else: netloc = "%s:%d" % (host, port) url = urlparse.urlunparse((scheme, netloc, path, params, query, fragment)) return url @staticmethod def _find_vmdk_url(lease_info, host, port): """Find the URL corresponding to a VMDK file in lease info.""" url = None ssl_thumbprint = None for deviceUrl in lease_info.deviceUrl: if deviceUrl.disk: url = VmdkHandle._fix_esx_url(deviceUrl.url, host, port) ssl_thumbprint = deviceUrl.sslThumbprint break if not url: excep_msg = _("Could not retrieve VMDK URL from lease info.") LOG.error(excep_msg) raise exceptions.VimException(excep_msg) LOG.debug("Found VMDK URL: %s from lease info.", url) return url, ssl_thumbprint class VmdkWriteHandle(VmdkHandle): """VMDK write handle based on HttpNfcLease. This class creates a vApp in the specified resource pool and uploads the virtual disk contents. """ def __init__(self, session, host, port, rp_ref, vm_folder_ref, import_spec, vmdk_size, http_method='PUT'): """Initializes the VMDK write handle with input parameters. :param session: valid API session to ESX/VC server :param host: ESX/VC server IP address or host name :param port: port for connection :param rp_ref: resource pool into which the backing VM is imported :param vm_folder_ref: VM folder in ESX/VC inventory to use as parent of backing VM :param import_spec: import specification of the backing VM :param vmdk_size: size of the backing VM's VMDK file :param http_method: either PUT or POST :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException, VimConnectionException, ValueError """ self._vmdk_size = vmdk_size self._bytes_written = 0 # Get lease and its info for vApp import lease, lease_info = self._create_import_vapp_lease(session, rp_ref, import_spec, vm_folder_ref) # Find VMDK URL where data is to be written url, thumbprint = self._find_vmdk_url(lease_info, host, port) self._vm_ref = lease_info.entity cookies = session.vim.client.options.transport.cookiejar # Create HTTP connection to write to VMDK URL if http_method == 'PUT': overwrite = 't' content_type = 'binary/octet-stream' elif http_method == 'POST': overwrite = None content_type = 'application/x-vnd.vmware-streamVmdk' else: raise ValueError('http_method must be either PUT or POST') self._conn = self._create_write_connection(http_method, url, vmdk_size, cookies=cookies, overwrite=overwrite, content_type=content_type, ssl_thumbprint=thumbprint) super(VmdkWriteHandle, self).__init__(session, lease, url, self._conn) def get_imported_vm(self): """"Get managed object reference of the VM created for import.""" return self._vm_ref def write(self, data): """Write data to the file. :param data: data to be written :raises: VimConnectionException, VimException """ try: self._file_handle.send(data) self._bytes_written += len(data) except requests.RequestException as excep: excep_msg = _("Connection error occurred while writing data to" " %s.") % self._url LOG.exception(excep_msg) raise exceptions.VimConnectionException(excep_msg, excep) except Exception as excep: # TODO(vbala) We need to catch and raise specific exceptions # related to connection problems, invalid request and invalid # arguments. excep_msg = _("Error occurred while writing data to" " %s.") % self._url LOG.exception(excep_msg) raise exceptions.VimException(excep_msg, excep) def close(self): """Releases the lease and close the connection. :raises: VimAttributeException, VimSessionOverLoadException, VimConnectionException """ try: self._release_lease() except exceptions.VimException: LOG.warning("Error occurred while releasing the lease " "for %s.", self._url, exc_info=True) super(VmdkWriteHandle, self).close() LOG.debug("Closed VMDK write handle for %s.", self._url) def _get_progress(self): return float(self._bytes_written) / self._vmdk_size * 100 def __str__(self): return "VMDK write handle for %s" % self._url class VmdkReadHandle(VmdkHandle): """VMDK read handle based on HttpNfcLease.""" def __init__(self, session, host, port, vm_ref, vmdk_path, vmdk_size): """Initializes the VMDK read handle with the given parameters. During the read (export) operation, the VMDK file is converted to a stream-optimized sparse disk format. Therefore, the size of the VMDK file read may be smaller than the actual VMDK size. :param session: valid api session to ESX/VC server :param host: ESX/VC server IP address or host name :param port: port for connection :param vm_ref: managed object reference of the backing VM whose VMDK is to be exported :param vmdk_path: path of the VMDK file to be exported :param vmdk_size: actual size of the VMDK file :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException, VimConnectionException """ self._vmdk_size = vmdk_size self._bytes_read = 0 # Obtain lease for VM export lease, lease_info = self._create_export_vm_lease(session, vm_ref) # find URL of the VMDK file to be read and open connection url, thumbprint = self._find_vmdk_url(lease_info, host, port) cookies = session.vim.client.options.transport.cookiejar self._conn = self._create_read_connection(url, cookies=cookies, ssl_thumbprint=thumbprint) super(VmdkReadHandle, self).__init__(session, lease, url, self._conn) def read(self, chunk_size): """Read a chunk of data from the VMDK file. :param chunk_size: size of read chunk :returns: the data :raises: VimException """ try: data = self._file_handle.read(READ_CHUNKSIZE) self._bytes_read += len(data) return data except Exception as excep: # TODO(vbala) We need to catch and raise specific exceptions # related to connection problems, invalid request and invalid # arguments. excep_msg = _("Error occurred while reading data from" " %s.") % self._url LOG.exception(excep_msg) raise exceptions.VimException(excep_msg, excep) def close(self): """Releases the lease and close the connection. :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException, VimConnectionException """ try: self._release_lease() except exceptions.VimException: LOG.warning("Error occurred while releasing the lease " "for %s.", self._url, exc_info=True) raise finally: super(VmdkReadHandle, self).close() LOG.debug("Closed VMDK read handle for %s.", self._url) def _get_progress(self): return float(self._bytes_read) / self._vmdk_size * 100 def __str__(self): return "VMDK read handle for %s" % self._url class ImageReadHandle(object): """Read handle for glance images.""" def __init__(self, glance_read_iter): """Initializes the read handle with given parameters. :param glance_read_iter: iterator to read data from glance image """ self._glance_read_iter = glance_read_iter self._iter = self.get_next() def read(self, chunk_size): """Read an item from the image data iterator. The input chunk size is ignored since the client ImageBodyIterator uses its own chunk size. """ try: data = next(self._iter) return data except StopIteration: LOG.debug("Completed reading data from the image iterator.") return "" def get_next(self): """Get the next item from the image iterator.""" for data in self._glance_read_iter: yield data def close(self): """Close the read handle. This is a NOP. """ pass def __str__(self): return "Image read handle" oslo.vmware-2.26.0/oslo_vmware/version.py0000666000175100017510000000126313224676076020516 0ustar zuulzuul00000000000000# Copyright 2016 OpenStack Foundation # # 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. import pbr.version version_info = pbr.version.VersionInfo('oslo.vmware') oslo.vmware-2.26.0/oslo_vmware/service.py0000666000175100017510000004256313224676076020501 0ustar zuulzuul00000000000000# Copyright (c) 2014 VMware, Inc. # All Rights Reserved. # # 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. """ Common classes that provide access to vSphere services. """ import logging import os import netaddr from oslo_utils import timeutils from oslo_utils import uuidutils import requests import six import six.moves.http_client as httplib import suds from suds import cache from suds import client from suds import plugin import suds.sax.element as element from suds import transport from oslo_vmware._i18n import _ from oslo_vmware import exceptions from oslo_vmware import vim_util CACHE_TIMEOUT = 60 * 60 # One hour cache timeout ADDRESS_IN_USE_ERROR = 'Address already in use' CONN_ABORT_ERROR = 'Software caused connection abort' RESP_NOT_XML_ERROR = 'Response is "text/html", not "text/xml"' SERVICE_INSTANCE = 'ServiceInstance' LOG = logging.getLogger(__name__) class ServiceMessagePlugin(plugin.MessagePlugin): """Suds plug-in handling some special cases while calling VI SDK.""" # list of XML elements which are allowed to be empty EMPTY_ELEMENTS = ["VirtualMachineEmptyProfileSpec"] def add_attribute_for_value(self, node): """Helper to handle AnyType. Suds does not handle AnyType properly. But VI SDK requires type attribute to be set when AnyType is used. :param node: XML value node """ if node.name == 'value' or node.name == 'val': node.set('xsi:type', 'xsd:string') # removeKey may be a 'int' or a 'string' if node.name == 'removeKey': try: int(node.text) node.set('xsi:type', 'xsd:int') except (ValueError, TypeError): node.set('xsi:type', 'xsd:string') def prune(self, el): pruned = [] for c in el.children: self.prune(c) if c.isempty(False) and c.name not in self.EMPTY_ELEMENTS: pruned.append(c) for p in pruned: el.children.remove(p) def marshalled(self, context): """Modifies the envelope document before it is sent. This method provides the plug-in with the opportunity to prune empty nodes and fix nodes before sending it to the server. :param context: send context """ # Suds builds the entire request object based on the WSDL schema. # VI SDK throws server errors if optional SOAP nodes are sent # without values; e.g., as opposed to test. self.prune(context.envelope) context.envelope.walk(self.add_attribute_for_value) class Response(six.BytesIO): """Response with an input stream as source.""" def __init__(self, stream, status=200, headers=None): self.status = status self.headers = headers or {} self.reason = requests.status_codes._codes.get( status, [''])[0].upper().replace('_', ' ') six.BytesIO.__init__(self, stream) @property def _original_response(self): return self @property def msg(self): return self def read(self, chunk_size, **kwargs): return six.BytesIO.read(self, chunk_size) def info(self): return self def get_all(self, name, default): result = self.headers.get(name) if not result: return default return [result] def getheaders(self, name): return self.get_all(name, []) def release_conn(self): self.close() class LocalFileAdapter(requests.adapters.HTTPAdapter): """Transport adapter for local files. See http://stackoverflow.com/a/22989322 """ def __init__(self, pool_maxsize=10): super(LocalFileAdapter, self).__init__(pool_connections=pool_maxsize, pool_maxsize=pool_maxsize) def _build_response_from_file(self, request): file_path = request.url[7:] with open(file_path, 'r') as f: buff = bytearray(os.path.getsize(file_path)) f.readinto(buff) resp = Response(buff) return self.build_response(request, resp) def send(self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None): return self._build_response_from_file(request) class RequestsTransport(transport.Transport): def __init__(self, cacert=None, insecure=True, pool_maxsize=10, connection_timeout=None): 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.session.mount('file:///', LocalFileAdapter(pool_maxsize=pool_maxsize)) self.session.mount('https://', requests.adapters.HTTPAdapter( pool_connections=pool_maxsize, pool_maxsize=pool_maxsize)) self.cookiejar = self.session.cookies self._connection_timeout = connection_timeout def open(self, request): resp = self.session.get(request.url, verify=self.verify) return six.BytesIO(resp.content) def send(self, request): resp = self.session.post(request.url, data=request.message, headers=request.headers, verify=self.verify, timeout=self._connection_timeout) return transport.Reply(resp.status_code, resp.headers, resp.content) class MemoryCache(cache.ObjectCache): def __init__(self): self._cache = {} def get(self, key): """Retrieves the value for a key or None.""" now = timeutils.utcnow_ts() for k in list(self._cache): (timeout, _value) = self._cache[k] if timeout and now >= timeout: del self._cache[k] return self._cache.get(key, (0, None))[1] def put(self, key, value, time=CACHE_TIMEOUT): """Sets the value for a key.""" timeout = 0 if time != 0: timeout = timeutils.utcnow_ts() + time self._cache[key] = (timeout, value) return True _CACHE = MemoryCache() class Service(object): """Base class containing common functionality for invoking vSphere services """ def __init__(self, wsdl_url=None, soap_url=None, cacert=None, insecure=True, pool_maxsize=10, connection_timeout=None, op_id_prefix='oslo.vmware'): self.wsdl_url = wsdl_url self.soap_url = soap_url self.op_id_prefix = op_id_prefix LOG.debug("Creating suds client with soap_url='%s' and wsdl_url='%s'", self.soap_url, self.wsdl_url) transport = RequestsTransport(cacert=cacert, insecure=insecure, pool_maxsize=pool_maxsize, connection_timeout=connection_timeout) self.client = client.Client(self.wsdl_url, transport=transport, location=self.soap_url, plugins=[ServiceMessagePlugin()], cache=_CACHE) self._service_content = None self._vc_session_cookie = None @staticmethod def build_base_url(protocol, host, port): proto_str = '%s://' % protocol host_str = '[%s]' % host if netaddr.valid_ipv6(host) else host port_str = '' if port is None else ':%d' % port return proto_str + host_str + port_str @staticmethod def _retrieve_properties_ex_fault_checker(response): """Checks the RetrievePropertiesEx API response for errors. Certain faults are sent in the SOAP body as a property of missingSet. This method raises VimFaultException when a fault is found in the response. :param response: response from RetrievePropertiesEx API call :raises: VimFaultException """ fault_list = [] details = {} if not response: # This is the case when the session has timed out. ESX SOAP # server sends an empty RetrievePropertiesExResponse. Normally # missingSet in the response objects has the specifics about # the error, but that's not the case with a timed out idle # session. It is as bad as a terminated session for we cannot # use the session. Therefore setting fault to NotAuthenticated # fault. LOG.debug("RetrievePropertiesEx API response is empty; setting " "fault to %s.", exceptions.NOT_AUTHENTICATED) fault_list = [exceptions.NOT_AUTHENTICATED] else: for obj_cont in response.objects: if hasattr(obj_cont, 'missingSet'): for missing_elem in obj_cont.missingSet: f_type = missing_elem.fault.fault f_name = f_type.__class__.__name__ fault_list.append(f_name) if f_name == exceptions.NO_PERMISSION: details['object'] = f_type.object.value details['privilegeId'] = f_type.privilegeId if fault_list: fault_string = _("Error occurred while calling " "RetrievePropertiesEx.") raise exceptions.VimFaultException(fault_list, fault_string, details=details) def _set_soap_headers(self, op_id): """Set SOAP headers for the next remote call to vCenter. SOAP headers may include operation ID and vcSessionCookie. The operation ID is a random string which allows to correlate log messages across different systems (OpenStack, vCenter, ESX). vcSessionCookie is needed when making PBM calls. """ headers = [] if self._vc_session_cookie: elem = element.Element('vcSessionCookie').setText( self._vc_session_cookie) headers.append(elem) if op_id: elem = element.Element('operationID').setText(op_id) headers.append(elem) if headers: self.client.set_options(soapheaders=headers) @property def service_content(self): if self._service_content is None: self._service_content = self.retrieve_service_content() return self._service_content def get_http_cookie(self): """Return the vCenter session cookie.""" cookies = self.client.options.transport.cookiejar for cookie in cookies: if cookie.name.lower() == 'vmware_soap_session': return cookie.value def __getattr__(self, attr_name): """Returns the method to invoke API identified by param attr_name.""" def request_handler(managed_object, **kwargs): """Handler for vSphere API calls. Invokes the API and parses the response for fault checking and other errors. :param managed_object: managed object reference argument of the API call :param kwargs: keyword arguments of the API call :returns: response of the API call :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException, VimConnectionException """ try: if isinstance(managed_object, str): # For strings, use string value for value and type # of the managed object. managed_object = vim_util.get_moref(managed_object, managed_object) if managed_object is None: return skip_op_id = kwargs.pop('skip_op_id', False) op_id = None if not skip_op_id: # Generate opID. It will appear in vCenter and ESX logs for # this particular remote call. op_id = '%s-%s' % (self.op_id_prefix, uuidutils.generate_uuid()) LOG.debug('Invoking %s.%s with opID=%s', managed_object._type, attr_name, op_id) self._set_soap_headers(op_id) request = getattr(self.client.service, attr_name) response = request(managed_object, **kwargs) if (attr_name.lower() == 'retrievepropertiesex'): Service._retrieve_properties_ex_fault_checker(response) return response except exceptions.VimFaultException: # Catch the VimFaultException that is raised by the fault # check of the SOAP response. raise except suds.WebFault as excep: fault_string = None if excep.fault: fault_string = excep.fault.faultstring doc = excep.document detail = None if doc is not None: detail = doc.childAtPath('/detail') if not detail: # NOTE(arnaud): this is needed with VC 5.1 detail = doc.childAtPath('/Envelope/Body/Fault/detail') fault_list = [] details = {} if detail: for fault in detail.getChildren(): fault_type = fault.get('type') if fault_type.endswith(exceptions.SECURITY_ERROR): fault_type = exceptions.NOT_AUTHENTICATED fault_list.append(fault_type) for child in fault.getChildren(): details[child.name] = child.getText() raise exceptions.VimFaultException(fault_list, fault_string, excep, details) except AttributeError as excep: raise exceptions.VimAttributeException( _("No such SOAP method %s.") % attr_name, excep) except (httplib.CannotSendRequest, httplib.ResponseNotReady, httplib.CannotSendHeader) as excep: raise exceptions.VimSessionOverLoadException( _("httplib error in %s.") % attr_name, excep) except requests.RequestException as excep: raise exceptions.VimConnectionException( _("requests error in %s.") % attr_name, excep) except Exception as excep: # TODO(vbala) should catch specific exceptions and raise # appropriate VimExceptions. # Socket errors which need special handling; some of these # might be caused by server API call overload. if (six.text_type(excep).find(ADDRESS_IN_USE_ERROR) != -1 or six.text_type(excep).find(CONN_ABORT_ERROR)) != -1: raise exceptions.VimSessionOverLoadException( _("Socket error in %s.") % attr_name, excep) # Type error which needs special handling; it might be caused # by server API call overload. elif six.text_type(excep).find(RESP_NOT_XML_ERROR) != -1: raise exceptions.VimSessionOverLoadException( _("Type error in %s.") % attr_name, excep) else: raise exceptions.VimException( _("Exception in %s.") % attr_name, excep) return request_handler def __repr__(self): return "vSphere object" def __str__(self): return "vSphere object" class SudsLogFilter(logging.Filter): """Filter to mask/truncate vCenter credentials in suds logs.""" def filter(self, record): if not hasattr(record.msg, 'childAtPath'): return True # Suds will log vCenter credentials if SessionManager.Login or # SessionManager.SessionIsActive fails. login = (record.msg.childAtPath('/Envelope/Body/Login') or record.msg.childAtPath('/Envelope/Body/SessionIsActive')) if login is None: return True if login.childAtPath('userName') is not None: login.childAtPath('userName').setText('***') if login.childAtPath('password') is not None: # nosec login.childAtPath('password').setText('***') # nosec session_id = login.childAtPath('sessionID') if session_id is not None: session_id.setText(session_id.getText()[-5:]) return True # Set log filter to mask/truncate vCenter credentials in suds logs. suds.client.log.addFilter(SudsLogFilter()) oslo.vmware-2.26.0/oslo_vmware/objects/0000775000175100017510000000000013224676314020077 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/oslo_vmware/objects/__init__.py0000666000175100017510000000000013224676076022205 0ustar zuulzuul00000000000000oslo.vmware-2.26.0/oslo_vmware/objects/datastore.py0000666000175100017510000003241313224676076022451 0ustar zuulzuul00000000000000# Copyright (c) 2014 VMware, Inc. # # 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. import logging import posixpath import random import six.moves.http_client as httplib import six.moves.urllib.parse as urlparse from oslo_vmware._i18n import _ from oslo_vmware import constants from oslo_vmware import exceptions from oslo_vmware import vim_util LOG = logging.getLogger(__name__) def get_datastore_by_ref(session, ds_ref): """Returns a datastore object for a given reference. :param session: a vmware api session object :param ds_ref: managed object reference of a datastore :rtype: a datastore object """ lst_properties = ["summary.type", "summary.name", "summary.capacity", "summary.freeSpace", "summary.uncommitted"] props = session.invoke_api( vim_util, "get_object_properties_dict", session.vim, ds_ref, lst_properties) # TODO(sabari): Instantiate with datacenter info. return Datastore(ds_ref, props["summary.name"], capacity=props.get("summary.capacity"), freespace=props.get("summary.freeSpace"), uncommitted=props.get("summary.uncommitted"), type=props.get("summary.type")) class Datastore(object): def __init__(self, ref, name, capacity=None, freespace=None, uncommitted=None, type=None, datacenter=None): """Datastore object holds ref and name together for convenience. :param ref: a vSphere reference to a datastore :param name: vSphere unique name for this datastore :param capacity: (optional) capacity in bytes of this datastore :param freespace: (optional) free space in bytes of datastore :param uncommitted: (optional) Total additional storage space in bytes of datastore :param type: (optional) datastore type :param datacenter: (optional) oslo_vmware Datacenter object """ if name is None: raise ValueError(_("Datastore name cannot be None")) if ref is None: raise ValueError(_("Datastore reference cannot be None")) if freespace is not None and capacity is None: raise ValueError(_("Invalid capacity")) if capacity is not None and freespace is not None: if capacity < freespace: raise ValueError(_("Capacity is smaller than free space")) self.ref = ref self.name = name self.capacity = capacity self.freespace = freespace self.uncommitted = uncommitted self.type = type self.datacenter = datacenter def build_path(self, *paths): """Constructs and returns a DatastorePath. :param paths: list of path components, for constructing a path relative to the root directory of the datastore :return: a DatastorePath object """ return DatastorePath(self.name, *paths) def build_url(self, scheme, server, rel_path, datacenter_name=None): """Constructs and returns a DatastoreURL. :param scheme: scheme of the URL (http, https). :param server: hostname or ip :param rel_path: relative path of the file on the datastore :param datacenter_name: (optional) datacenter name :return: a DatastoreURL object """ if self.datacenter is None and datacenter_name is None: raise ValueError(_("datacenter must be set to build url")) if datacenter_name is None: datacenter_name = self.datacenter.name return DatastoreURL(scheme, server, rel_path, datacenter_name, self.name) def __str__(self): return '[%s]' % self.name def get_summary(self, session): """Get datastore summary. :param datastore: Reference to the datastore :return: 'summary' property of the datastore """ return session.invoke_api(vim_util, 'get_object_property', session.vim, self.ref, 'summary') def get_connected_hosts(self, session): """Get a list of usable (accessible, mounted, read-writable) hosts where the datastore is mounted. :param: session: session :return: list of HostSystem managed object references """ hosts = [] summary = self.get_summary(session) if not summary.accessible: return hosts host_mounts = session.invoke_api(vim_util, 'get_object_property', session.vim, self.ref, 'host') if not hasattr(host_mounts, 'DatastoreHostMount'): return hosts for host_mount in host_mounts.DatastoreHostMount: if self.is_datastore_mount_usable(host_mount.mountInfo): hosts.append(host_mount.key) connectables = [] if hosts: host_runtimes = session.invoke_api( vim_util, 'get_properties_for_a_collection_of_objects', session.vim, 'HostSystem', hosts, ['runtime']) for host_object in host_runtimes.objects: host_props = vim_util.propset_dict(host_object.propSet) host_runtime = host_props.get('runtime') if hasattr(host_runtime, 'inMaintenanceMode') and ( not host_runtime.inMaintenanceMode): connectables.append(host_object.obj) return connectables @staticmethod def is_datastore_mount_usable(mount_info): """Check if a datastore is usable as per the given mount info. The datastore is considered to be usable for a host only if it is writable, mounted and accessible. :param mount_info: HostMountInfo data object :return: True if datastore is usable """ writable = mount_info.accessMode == 'readWrite' mounted = getattr(mount_info, 'mounted', True) accessible = getattr(mount_info, 'accessible', False) return writable and mounted and accessible @staticmethod def choose_host(hosts): if not hosts: return None i = random.SystemRandom().randrange(0, len(hosts)) return hosts[i] class DatastorePath(object): """Class for representing a directory or file path in a vSphere datatore. This provides various helper methods to access components and useful variants of the datastore path. Example usage: DatastorePath("datastore1", "_base/foo", "foo.vmdk") creates an object that describes the "[datastore1] _base/foo/foo.vmdk" datastore file path to a virtual disk. Note: - Datastore path representations always uses forward slash as separator (hence the use of the posixpath module). - Datastore names are enclosed in square brackets. - Path part of datastore path is relative to the root directory of the datastore, and is always separated from the [ds_name] part with a single space. """ def __init__(self, datastore_name, *paths): if datastore_name is None or datastore_name == '': raise ValueError(_("Datastore name cannot be empty")) self._datastore_name = datastore_name self._rel_path = '' if paths: if None in paths: raise ValueError(_("Path component cannot be None")) self._rel_path = posixpath.join(*paths) def __str__(self): """Full datastore path to the file or directory.""" if self._rel_path != '': return "[%s] %s" % (self._datastore_name, self.rel_path) return "[%s]" % self._datastore_name @property def datastore(self): return self._datastore_name @property def parent(self): return DatastorePath(self.datastore, posixpath.dirname(self._rel_path)) @property def basename(self): return posixpath.basename(self._rel_path) @property def dirname(self): return posixpath.dirname(self._rel_path) @property def rel_path(self): return self._rel_path def join(self, *paths): """Join one or more path components intelligently into a datastore path. If any component is an absolute path, all previous components are thrown away, and joining continues. The return value is the concatenation of the paths with exactly one slash ('/') inserted between components, unless p is empty. :return: A datastore path """ if paths: if None in paths: raise ValueError(_("Path component cannot be None")) return DatastorePath(self.datastore, self._rel_path, *paths) return self def __eq__(self, other): return (isinstance(other, DatastorePath) and self._datastore_name == other._datastore_name and self._rel_path == other._rel_path) @classmethod def parse(cls, datastore_path): """Constructs a DatastorePath object given a datastore path string.""" if not datastore_path: raise ValueError(_("Datastore path cannot be empty")) spl = datastore_path.split('[', 1)[1].split(']', 1) path = "" if len(spl) == 1: datastore_name = spl[0] else: datastore_name, path = spl return cls(datastore_name, path.strip()) class DatastoreURL(object): """Class for representing a URL to HTTP access a file in a datastore. This provides various helper methods to access components and useful variants of the datastore URL. """ def __init__(self, scheme, server, path, datacenter_path, datastore_name): self._scheme = scheme self._server = server self._path = path self._datacenter_path = datacenter_path self._datastore_name = datastore_name params = {'dcPath': self._datacenter_path, 'dsName': self._datastore_name} self._query = urlparse.urlencode(params) @classmethod def urlparse(cls, url): scheme, server, path, params, query, fragment = urlparse.urlparse(url) if not query: path = path.split('?') query = path[1] path = path[0] params = urlparse.parse_qs(query) dc_path = params.get('dcPath') if dc_path is not None and len(dc_path) > 0: datacenter_path = dc_path[0] ds_name = params.get('dsName') if ds_name is not None and len(ds_name) > 0: datastore_name = ds_name[0] path = path[len('/folder'):] return cls(scheme, server, path, datacenter_path, datastore_name) @property def path(self): return self._path.strip('/') @property def datacenter_path(self): return self._datacenter_path @property def datastore_name(self): return self._datastore_name def __str__(self): return '%s://%s/folder/%s?%s' % (self._scheme, self._server, self.path, self._query) def connect(self, method, content_length, cookie): try: if self._scheme == 'http': conn = httplib.HTTPConnection(self._server) elif self._scheme == 'https': # TODO(browne): This needs to be changed to use python requests conn = httplib.HTTPSConnection(self._server) # nosec else: excep_msg = _("Invalid scheme: %s.") % self._scheme LOG.error(excep_msg) raise ValueError(excep_msg) conn.putrequest(method, '/folder/%s?%s' % (self.path, self._query)) conn.putheader('User-Agent', constants.USER_AGENT) conn.putheader('Content-Length', content_length) conn.putheader('Cookie', cookie) conn.endheaders() LOG.debug("Created HTTP connection to transfer the file with " "URL = %s.", str(self)) return conn except (httplib.InvalidURL, httplib.CannotSendRequest, httplib.CannotSendHeader) as excep: excep_msg = _("Error occurred while creating HTTP connection " "to write to file with URL = %s.") % str(self) LOG.exception(excep_msg) raise exceptions.VimConnectionException(excep_msg, excep) def get_transfer_ticket(self, session, method): client_factory = session.vim.client.factory spec = vim_util.get_http_service_request_spec(client_factory, method, str(self)) ticket = session.invoke_api( session.vim, 'AcquireGenericServiceTicket', session.vim.service_content.sessionManager, spec=spec) return '%s="%s"' % (constants.CGI_COOKIE_KEY, ticket.id) oslo.vmware-2.26.0/oslo_vmware/objects/datacenter.py0000666000175100017510000000177013224676076022577 0ustar zuulzuul00000000000000# Copyright (c) 2014 VMware, Inc. # # 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_vmware._i18n import _ class Datacenter(object): def __init__(self, ref, name): """Datacenter object holds ref and name together for convenience.""" if name is None: raise ValueError(_("Datacenter name cannot be None")) if ref is None: raise ValueError(_("Datacenter reference cannot be None")) self.ref = ref self.name = name oslo.vmware-2.26.0/ChangeLog0000664000175100017510000003706613224676313015676 0ustar zuulzuul00000000000000CHANGES ======= 2.26.0 ------ * Cleanup test-requirements * Updated from global requirements * Fix the repeating titles 2.25.0 ------ * Remove -U from pip install * Avoid tox\_install.sh for constraints support * Remove setting of version/release from releasenotes * Updated from global requirements * Updated from global requirements * Imported Translations from Zanata 2.24.0 ------ * Updated from global requirements * Updated from global requirements * Imported Translations from Zanata * Updated from global requirements * Update reno for stable/pike * Updated from global requirements 2.23.0 ------ * Update URLs in documents according to document migration * rearrange existing documentation to fit the new standard layout * Switch from oslosphinx to openstackdocstheme * Enable warning-is-error in doc build * Use request\_handler() for ExtensionManager APIs * Improving cover testenv 2.22.1 ------ * Add hacking rule to prevent log translations 2.22.0 ------ * Updated from global requirements 2.21.0 ------ * Do not prune some special XML elements which are empty * Updated from global requirements 2.20.0 ------ * Updated from global requirements * Wrong member variable name in class Datastore * Make sure host in maintenance mode excluded from image upload * Updated from global requirements * Updated from global requirements * Updated from global requirements * Updated from global requirements * Updated from global requirements 2.19.0 ------ * Trivial: Remove testscenarios from test-requirements.txt * Remove log translations 2.18.0 ------ * Updated from global requirements * [Fix gate]Update test requirement * Updated from global requirements * Make the mock call in MemoryCacheTest forward-compatible * Updated from global requirements * Updated from global requirements * Improve unit test coverage * pbr.version.VersionInfo needs package name (oslo.xyz and not oslo\_xyz) * Add in support for removeKey * Use https for references to openstack.org * Update reno for stable/ocata 2.17.0 ------ * Fix setting the SOAP headers for remote calls * Remove references to Python 3.4 * Updated from global requirements * Remove some useless log messages * Add operation ID for remote calls * Fix image meta-data update for Glance v2 * Add Constraints support * Updated from global requirements 2.16.0 ------ * Files with no code must be left completely empty * Add 'uncommitted' field to the 'Datastore' class * Add SPBM WSDL for vSphere 6.5 * Show team and repo badges on README * Updated from global requirements * Updated from global requirements * Updated from global requirements * Trivial fixes to the usage doc * Imported Translations from Zanata * Imported Translations from Zanata * Updated from global requirements * Remove mox3 in test-requirement.txt * [TrivialFix] Replace 'assertTrue(a in b)' with 'assertIn(a, b)' * Updated from global requirements 2.15.0 ------ * Enable release notes translation * Updated from global requirements * Updated from global requirements * Updated from global requirements * Updated from global requirements * Update home page link in cfg file * Updated from global requirements * Set pool size for HTTPS connections * Update reno for stable/newton * Improve logging for task updates 2.14.0 ------ * Updated from global requirements * Fix TypeError:six.StringIO(resp.content) must be str or None, not bytes 2.13.0 ------ * Method to download file to VMware server * Pass connection timeout so that invoke\_api will not wait forever 2.12.0 ------ * Updated from global requirements * Add http\_method to download\_stream\_optimized\_data * Refactor the image transfer * Remove discover from test-requirements * Updated from global requirements 2.11.0 ------ * Updated from global requirements * Add a py35 tox venv for upcoming py35 support * Updated from global requirements * Remove unnecessary properties from image-meta * Updated from global requirements * Updated from global requirements 2.10.0 ------ * Updated from global requirements 2.9.0 ----- * Imported Translations from Zanata 2.8.0 ----- * Updated from global requirements * Updated from global requirements * Updated from global requirements * Refactor VmdkWriteHandle and VmdkReadHandle * Updated from global requirements * Add reno for release notes management * Support download of virtual disk in ova container * Updated from global requirements * Updated from global requirements 2.7.0 ----- * Updated from global requirements * Trivial: ignore openstack/common in flake8 exclude list 2.6.0 ----- * Updated from global requirements * Imported Translations from Zanata * Updated from global requirements * Updated from global requirements * Should not raise Exception before connection close * Remove explicit use of asserts * Move bandit into pep8 2.5.0 ----- * Updated from global requirements * Updated from global requirements * Updated from global requirements 2.4.0 ----- * Updated from global requirements 2.3.0 ----- * Remove bandit.yaml in favor of defaults * Updated from global requirements * Update translation setup * Updated from global requirements * Updated from global requirements * Updated from global requirements * Imported Translations from Zanata * Updated from global requirements * Use oslo\_utils reflection to get function name 2.2.1 ----- * Python 3 deprecated the logger.warn method in favor of warning * Put py34 first in the env order of tox * assertIsNone(val) instead of assertEqual(None,val) 2.2.0 ----- * isoformat instead of deprecated timeutils.strtime * Overwrite VMwareDriverException message * Updated from global requirements * Updated from global requirements * Clean requirements * Trival: Remove 'MANIFEST.in' 2.1.0 ----- * Updated from global requirements 2.0.0 ----- * Updated from global requirements * Updated from global requirements * Remove python 2.6 classifier * Remove python 2.6 and cleanup tox.ini * Imported Translations from Zanata * Updated from global requirements 1.23.0 ------ * Updated from global requirements 1.22.0 ------ * Updated from global requirements * Fix coverage configuration and execution * No need for Oslo Incubator Sync * Add support for SSL thumbprint when using FileWriteHandle * Define WithRetrieval context * Imported Translations from Zanata * Add shields.io version/downloads links/badges into README.rst * docs - Set pbr 'warnerrors' option for doc build * clean up auto-doc api build * do not show the full release history in the main table of contents * clean up readme and documentation titles * Change ignore-errors to ignore\_errors * Updated from global requirements * Add support for using HTTP POST in VmdkWriteHandle * Add unit tests for FileHandle.\_create\_connection * Update the documentation 1.21.0 ------ * Updated from global requirements * Use SSL thumbprints for NFC transfer * Updated from global requirements * Update bandit.yaml to the latest 1.20.0 ------ * Updated from global requirements * Updated from global requirements * Updated from global requirements * flake8 - remove unused rules * Imported Translations from Transifex * Allow http connection pool size to be configured 1.19.0 ------ * Imported Translations from Transifex * Imported Translations from Transifex 1.18.0 ------ * Make usage doc easier to understand for library consumers * Updated from global requirements * Imported Translations from Transifex * Updated from global requirements * Updated from global requirements * Remove username from logs * Updated from global requirements 1.17.0 ------ * Update log level of session related logs * Fix mock calls * Updated from global requirements * Mask/truncate vCenter credentials in suds logs * Imported Translations from Transifex 1.16.0 ------ * Add tox target to find missing requirements * Updated from global requirements * Fix exception to string in py34 * Updated from global requirements 0.15.0 ------ * Updated from global requirements * Updated from global requirements * Add in support for ManagedObjectNotFound exception * Deprecate unused exceptions * Raise VimFaultException for unknown faults * Exception hierarchy refactoring * Updated from global requirements * Update progress lease every 60 seconds * Updated from global requirements * Fix bandit tox environment to properly run * Updated from global requirements 0.14.0 ------ * Remove oslo namespace package * Port test from Nova * Imported Translations from Transifex 0.13.1 ------ * Imported Translations from Transifex * Revert "Raise VimFaultException for unknown faults" 0.13.0 ------ * Add ToolsUnavailable exception * Add support for dynamicProperty * Remove support for Python 3.3 * Updated from global requirements * Remove run\_cross\_tests.sh * Use suds-jurko on Python 2 * Updated from global requirements * Imported Translations from Transifex * Updated from global requirements * Raise VimFaultException for unknown faults * Imported Translations from Transifex * Add NoDiskSpaceException * Add utility function to get profiles by IDs * Add bandit to tox for security static analysis * Add SPBM WSDL for vSphere 6.0 0.12.0 ------ * Uncap library requirements for liberty * Cleanup README.rst and setup.cfg * Update to latest hacking * Imported Translations from Transifex * Revert "VMWare NSXv: Common components" * Updated from global requirements * Move pylint dependency to tox.ini * Move exception related tests to new module 0.11.1 ------ * Switch to non-namespaced module imports * Imported Translations from Transifex * Move missing tests to new oslo\_vmware location 0.11.0 ------ * Imported Translations from Transifex * Add get\_datastore\_by\_ref method to oslo.vmware * Change use of random to random.SystemRandom 0.10.0 ------ * Imported Translations from Transifex * Updated from global requirements * Handle SPBM SecurityError * PBM utility method to retrieve profiles of a VM * Updated from global requirements * Imported Translations from Transifex * Add missing links to README * VMWare NSXv: Common components 0.9.0 ----- * Imported Translations from Transifex * Updated from global requirements * Correct usage to oslo\_vmware * Fix line wrapping * Updated from global requirements * Correct the doc usage example * Fix missing project name in installation doc * Imported Translations from Transifex * Imported Translations from Transifex * Fix race during session creation * Make setup.cfg packages include oslo.vmware * Updated from global requirements * Move files out of the namespace package * Use ToggleLazy fixture from oslo.i18n * Use \_is\_valid\_ipv6 from oslo.utils * Allow checking api session * Updated from global requirements * Updated from global requirements * Add pbr to installation requirements * Update urllib3 requirements * Workflow documentation is now in infra-manual * Fix re-create session during transient failures * Imported Translations from Transifex 0.8.0 ----- * Switch to use requests/urllib3 and enable cacert validation * Updated from global requirements * Updated from global requirements * Enable support for python 3.x * Updated from global requirements 0.7.0 ----- * Updated from global requirements * Imported Translations from Transifex * Activate pep8 check that \_ is imported * Do not log when reraising an exception * Imported Translations from Transifex * Updated from global requirements * Add unit test for VC 5.1 web fault handling * Fix to get exception detail with vCenter 5.1 * Download image API to bypass vCenter * Updated from global requirements * Updated from global requirements * Enable the PBM WSDL to be updated * Support building wheels (PEP-427) * Fixup autoindex.rst only if it exists * Supress error logs when exception is thrown * Fix handling of fault details * Fix UnboundLocalError during WebFault handling * Use faultstring attribute in suds.WebFault.fault * Imported Translations from Transifex * Fix the log message for progress * Add API to get the entity inventory path * VimExceptions need to support i18n objects * Switch to using oslo.utils * Use custom transport adapter for file URLs * getText can be called only when doc is not None * Updated from global requirements * Add unit test for suds cache expiration * Add a memory based shared cache 0.6.0 ----- * Updated from global requirements * VMware: Enable vCenter SSL certificate validation * Add DuplicateName exception * Add 'details' property to VMwareDriverException * Enable oslo.i18n for oslo.vmware * Add API to enable calling module to register an exception * Imported Translations from Transifex * Add docs target and generate api docs * Updated from global requirements * Work toward Python 3.4 support and testing * warn against sorting requirements * Add exception for TaskInProgress * Updated from global requirements * Refactoring to reduce noise in log files * Imported Translations from Transifex * Add missing session parameter to get\_summary * Updated from global requirements * Switch off caching to prevent cache poisoning by local attacker * Support for copying streamOptimized disk to file * Add support for the DatastoreURL object * Add methods to the Datastore objects * Imported Translations from Transifex * Add Pylint testenv environment 0.5.0 ----- * \_trunc\_id to check if the session\_id is not None * Port the Datastore and DatastorePath objects * Log additional details of suds faults * Enabled hacking check H305 * Imported Translations from Transifex * Add constant for ESX datacenter path (HTTP access) * Store PBM wsdl in the oslo.vmware git repository * Bump hacking to version 0.9.2 * Fix seek and tell in BlockingQueue * Add support for using extensions * The 'result' variable in RetryDecorator may be undefined * Imported Translations from Transifex * Fix docstrings of constructors * Do not log the full session ID * Refactor the PBM support * Fix wrong usage of assertRaises * Translations: make use of \_LE, \_LI and \_LW 0.4.0 ----- * Sync excutils from Oslo * Updated from global requirements * Use assertIsNone * Bump hacking to 0.9.x series * replace iterator.next() with next(iterator) * remove definitions of Python Source Code Encoding * Setup for translation * Updated from global requirements * cleaning up index.rst file * Add networkFolder in the traversal spec * Ensure port support does not break backward compatibility * replace string format arguments with function parameters * Support for IPv6 and Non-standard ports * Support 'InvalidPowerState' exception * Don't translate debug level logs in oslo-vmware * Updated from global requirements * Sync changes from Nova error\_util.py * Updated from global requirements * Remove \_\_del\_\_ usage in oslo.vmware driver * Add a test to oslo.vmware test\_image\_transfer * import run\_cross\_tests.sh from incubator * Fix vim25:InvalidRequest when no profiles exist * VMware: treat cases when SOAP reply does not have a body * Add unittest method "test\_download\_flat\_image" * Add missing unit tests for VMwareAPISession 0.3 --- * Updated from global requirements * Fix unit tests running unnecessarily slow * Enable download of streamOptimized file-like * Fix docstrings in VMwareAPISession * Ensure that the pbm is not None * PBM related utility methods * Add PBM client for policy based placement * Updated from global requirements * Replace unicode() for six.text\_type 0.2 --- * Remove dependency on log.py from openstack/common * Remove vim header 0.1 --- * VMware: raise more specific exceptions * Move image transfer code in VMware drivers to OSLO * Move read/write handles in VMware drivers to OSLO * Move API invocation code in VMware drivers to OSLO * Move VIM API client code in VMware drivers to OSLO * Add eventlet requirement * Move utility methods in VMware drivers to OSLO * Import necessary files from openstack.common * Initial checkin for Oslo VMware Library generated using oslo-cookiecutter oslo.vmware-2.26.0/requirements.txt0000666000175100017510000000115213224676076017403 0ustar zuulzuul00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. pbr!=2.1.0,>=2.0.0 # Apache-2.0 stevedore>=1.20.0 # Apache-2.0 netaddr>=0.7.18 # BSD six>=1.10.0 # MIT oslo.i18n>=3.15.3 # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0 # for the routing notifier PyYAML>=3.10 # MIT lxml!=3.7.0,>=3.4.1 # BSD suds-jurko>=0.6 # LGPLv3+ eventlet!=0.18.3,!=0.20.1,<0.21.0,>=0.18.2 # MIT requests>=2.14.2 # Apache-2.0 urllib3>=1.21.1 # MIT oslo.concurrency>=3.20.0 # Apache-2.0 oslo.vmware-2.26.0/LICENSE0000666000175100017510000002363613224676076015137 0ustar zuulzuul00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. oslo.vmware-2.26.0/AUTHORS0000664000175100017510000000414213224676313015161 0ustar zuulzuul00000000000000Akihiro Motoki Akihiro Motoki Aman Kumar Andreas Jaeger Andreas Jaeger Arnaud Legendre Arnaud Legendre Cao Xuan Hoang Cedric Brandily ChangBo Guo(gcb) Christian Berendt Dan Prince Davanum Srinivas Davanum Srinivas Doug Hellmann Doug Hellmann Eric Brown Flavio Percoco Gary Kotton Giridhar Jayavelu Haifeng.Yan James Carey Janonymous Javeme Javier Pena Jeremy Stanley Kirill Bespalov Kobi Samoray Luong Anh Tuan Masaru Nomura Matt Riedemann Monty Taylor Ngo Quoc Cuong Nguyen Van Trung OpenStack Release Bot Radoslav Gerganov Ronald Bradford Ryan Hsu Ryan Hsu Sabari Kumar Murugesan Steve Martinelli Swapnil Kulkarni (coolsvap) Tony Breeds Tracy Jones Victor Stinner Vipin Balachandran Vui Lam ZHU ZHU Zhongcheng Lao Zuul avnish howardlee int32bit loooosy melissaml ricolin zhang.lei zhangdaolong zhu.rong oslo.vmware-2.26.0/PKG-INFO0000664000175100017510000000371513224676314015214 0ustar zuulzuul00000000000000Metadata-Version: 1.1 Name: oslo.vmware Version: 2.26.0 Summary: Oslo VMware library Home-page: https://docs.openstack.org/oslo.vmware/latest/ Author: OpenStack Author-email: openstack-dev@lists.openstack.org License: UNKNOWN Description-Content-Type: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/badges/oslo.vmware.svg :target: https://governance.openstack.org/reference/tags/index.html .. Change things from this point on =================================================== oslo.vmware --- VMware support code for OpenStack =================================================== .. image:: https://img.shields.io/pypi/v/oslo.vmware.svg :target: https://pypi.python.org/pypi/oslo.vmware/ :alt: Latest Version .. image:: https://img.shields.io/pypi/dm/oslo.vmware.svg :target: https://pypi.python.org/pypi/oslo.vmware/ :alt: Downloads The Oslo VMware library provides support for common VMware operations and APIs. * License: Apache License, Version 2.0 * Documentation: https://docs.openstack.org/oslo.vmware/latest/ * Source: https://git.openstack.org/cgit/openstack/oslo.vmware * Bugs: https://bugs.launchpad.net/oslo.vmware Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 oslo.vmware-2.26.0/tox.ini0000666000175100017510000000352313224676076015436 0ustar zuulzuul00000000000000[tox] minversion = 2.0 envlist = py35,py27,pypy,pep8 [testenv] install_command = pip install {opts} {packages} whitelist_externals = find rm deps = -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} -r{toxinidir}/test-requirements.txt -r{toxinidir}/requirements.txt commands = python setup.py testr --slowest --testr-args='{posargs}' [testenv:pep8] commands = flake8 # Run security linter bandit -r oslo_vmware [testenv:pylint] deps = -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} -r{toxinidir}/test-requirements.txt -r{toxinidir}/requirements.txt pylint>=1.3.0 commands = pylint oslo [testenv:bandit] commands = bandit -r oslo_vmware [testenv:docs] commands = python setup.py build_sphinx [testenv:cover] commands = coverage erase find . -type f -name "*.pyc" -delete python setup.py test --coverage --coverage-package-name=oslo_vmware --testr-args='{posargs}' coverage report [testenv:venv] commands = {posargs} [flake8] show-source = True ignore = H405 exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,__init__.py [hacking] import_exceptions = oslo_vmware.tests.base tests.base local-check-factory = oslo_vmware.hacking.checks.factory [testenv:pip-missing-reqs] # do not install test-requirements as that will pollute the virtualenv for # determining missing packages # this also means that pip-missing-reqs must be installed separately, outside # of the requirements.txt files deps = pip_missing_reqs commands = pip-missing-reqs -d --ignore-module=oslo_vmware* --ignore-file=oslo_vmware/tests/* oslo_vmware [testenv:releasenotes] commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html oslo.vmware-2.26.0/.mailmap0000666000175100017510000000013013224676076015533 0ustar zuulzuul00000000000000# Format is: # # oslo.vmware-2.26.0/pylintrc0000666000175100017510000000165613224676076015717 0ustar zuulzuul00000000000000# The format of this file isn't really documented; just use --generate-rcfile [Messages Control] # C0111: Don't require docstrings on every method # W0511: TODOs in code comments are fine. # W0142: *args and **kwargs are fine. # W0622: Redefining id is fine. disable=C0111,W0511,W0142,W0622 [Basic] # Variable names can be 1 to 31 characters long, with lowercase and underscores variable-rgx=[a-z_][a-z0-9_]{0,30}$ # Argument names can be 2 to 31 characters long, with lowercase and underscores argument-rgx=[a-z_][a-z0-9_]{1,30}$ # Method names should be at least 3 characters long # and be lowercased with underscores method-rgx=([a-z_][a-z0-9_]{2,50}|setUp|tearDown)$ [Design] max-public-methods=100 min-public-methods=0 max-args=6 [Variables] # List of additional names supposed to be defined in builtins. Remember that # you should avoid to define new builtins when possible. # _ is used by our localization additional-builtins=_ oslo.vmware-2.26.0/releasenotes/0000775000175100017510000000000013224676314016602 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/releasenotes/source/0000775000175100017510000000000013224676314020102 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/releasenotes/source/conf.py0000666000175100017510000002154613224676076021420 0ustar zuulzuul00000000000000# -*- coding: utf-8 -*- # 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. # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'openstackdocstheme', 'reno.sphinxext', ] # openstackdocstheme options repository_name = 'openstack/oslo.vmware' bug_project = 'oslo.vmware' bug_tag = '' # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'oslo.vmware Release Notes' copyright = u'2016, oslo.vmware Developers' # Release notes do not need a version in the title, they span # multiple versions. # The full version, including alpha/beta/rc tags. release = '' # The short X.Y version. version = '' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'openstackdocs' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%Y-%m-%d %H:%M' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'oslo.vmwareReleaseNotesDoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'oslo.vmwareReleaseNotes.tex', u'oslo.vmware Release Notes Documentation', u'oslo.vmware Developers', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'oslo.vmwareReleaseNotes', u'oslo.vmware Release Notes Documentation', [u'oslo.vmware Developers'], 1) ] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'oslo.vmwareReleaseNotes', u'oslo.vmware Release Notes Documentation', u'oslo.vmware Developers', 'oslo.vmwareReleaseNotes', 'The Oslo VMware library provides support for common VMware operations' ' and APIs.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # texinfo_no_detailmenu = False # -- Options for Internationalization output ------------------------------ locale_dirs = ['locale/'] oslo.vmware-2.26.0/releasenotes/source/locale/0000775000175100017510000000000013224676314021341 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/releasenotes/source/locale/en_GB/0000775000175100017510000000000013224676314022313 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/releasenotes/source/locale/en_GB/LC_MESSAGES/0000775000175100017510000000000013224676314024100 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po0000666000175100017510000000213613224676076027142 0ustar zuulzuul00000000000000# Andi Chandler , 2016. #zanata # Andi Chandler , 2017. #zanata msgid "" msgstr "" "Project-Id-Version: oslo.vmware Release Notes 2.23.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-09-20 20:53+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2017-09-28 03:31+0000\n" "Last-Translator: Andi Chandler \n" "Language-Team: English (United Kingdom)\n" "Language: en-GB\n" "X-Generator: Zanata 3.9.6\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" msgid "2.8.0" msgstr "2.8.0" msgid "Newton Series Release Notes" msgstr "Newton Series Release Notes" msgid "Ocata Series Release Notes" msgstr "Ocata Series Release Notes" msgid "Other Notes" msgstr "Other Notes" msgid "Pike Series Release Notes" msgstr "Pike Series Release Notes" msgid "Switch to reno for managing release notes." msgstr "Switch to reno for managing release notes." msgid "Unreleased Release Notes" msgstr "Unreleased Release Notes" msgid "oslo.vmware Release Notes" msgstr "oslo.vmware Release Notes" oslo.vmware-2.26.0/releasenotes/source/locale/fr/0000775000175100017510000000000013224676314021750 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/releasenotes/source/locale/fr/LC_MESSAGES/0000775000175100017510000000000013224676314023535 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/releasenotes/source/locale/fr/LC_MESSAGES/releasenotes.po0000666000175100017510000000170613224676076026601 0ustar zuulzuul00000000000000# Gérald LONLAS , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: oslo.vmware Release Notes 2.15.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2016-10-22 08:58+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-10-22 06:06+0000\n" "Last-Translator: Gérald LONLAS \n" "Language-Team: French\n" "Language: fr\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" msgid "2.8.0" msgstr "2.8.0" msgid "Newton Series Release Notes" msgstr "Note de release pour Newton" msgid "Other Notes" msgstr "Autres notes" msgid "Switch to reno for managing release notes." msgstr "Commence à utiliser reno pour la gestion des notes de release" msgid "Unreleased Release Notes" msgstr "Note de release pour les changements non déployées" msgid "oslo.vmware Release Notes" msgstr "Note de release pour oslo.vmware" oslo.vmware-2.26.0/releasenotes/source/newton.rst0000666000175100017510000000021613224676076022154 0ustar zuulzuul00000000000000============================= Newton Series Release Notes ============================= .. release-notes:: :branch: origin/stable/newton oslo.vmware-2.26.0/releasenotes/source/_static/0000775000175100017510000000000013224676314021530 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/releasenotes/source/_static/.placeholder0000666000175100017510000000000013224676076024010 0ustar zuulzuul00000000000000oslo.vmware-2.26.0/releasenotes/source/unreleased.rst0000666000175100017510000000014413224676076022771 0ustar zuulzuul00000000000000========================== Unreleased Release Notes ========================== .. release-notes:: oslo.vmware-2.26.0/releasenotes/source/index.rst0000666000175100017510000000024113224676076021747 0ustar zuulzuul00000000000000=========================== oslo.vmware Release Notes =========================== .. toctree:: :maxdepth: 1 unreleased pike ocata newton oslo.vmware-2.26.0/releasenotes/source/ocata.rst0000666000175100017510000000021213224676076021725 0ustar zuulzuul00000000000000============================ Ocata Series Release Notes ============================ .. release-notes:: :branch: origin/stable/ocata oslo.vmware-2.26.0/releasenotes/source/_templates/0000775000175100017510000000000013224676314022237 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/releasenotes/source/_templates/.placeholder0000666000175100017510000000000013224676076024517 0ustar zuulzuul00000000000000oslo.vmware-2.26.0/releasenotes/source/pike.rst0000666000175100017510000000021713224676076021573 0ustar zuulzuul00000000000000=================================== Pike Series Release Notes =================================== .. release-notes:: :branch: stable/pike oslo.vmware-2.26.0/releasenotes/notes/0000775000175100017510000000000013224676314017732 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/releasenotes/notes/add_reno-3b4ae0789e9c45b4.yaml0000666000175100017510000000007113224676076024622 0ustar zuulzuul00000000000000--- other: - Switch to reno for managing release notes.oslo.vmware-2.26.0/.coveragerc0000666000175100017510000000016113224676076016237 0ustar zuulzuul00000000000000[run] branch = True source = oslo_vmware omit = oslo_vmware/tests/* [report] ignore_errors = True precision = 2 oslo.vmware-2.26.0/.testr.conf0000666000175100017510000000047613224676076016215 0ustar zuulzuul00000000000000[DEFAULT] test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \ OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \ OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \ ${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION test_id_option=--load-list $IDFILE test_list_option=--listoslo.vmware-2.26.0/README.rst0000666000175100017510000000171713224676076015615 0ustar zuulzuul00000000000000======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/badges/oslo.vmware.svg :target: https://governance.openstack.org/reference/tags/index.html .. Change things from this point on =================================================== oslo.vmware --- VMware support code for OpenStack =================================================== .. image:: https://img.shields.io/pypi/v/oslo.vmware.svg :target: https://pypi.python.org/pypi/oslo.vmware/ :alt: Latest Version .. image:: https://img.shields.io/pypi/dm/oslo.vmware.svg :target: https://pypi.python.org/pypi/oslo.vmware/ :alt: Downloads The Oslo VMware library provides support for common VMware operations and APIs. * License: Apache License, Version 2.0 * Documentation: https://docs.openstack.org/oslo.vmware/latest/ * Source: https://git.openstack.org/cgit/openstack/oslo.vmware * Bugs: https://bugs.launchpad.net/oslo.vmware oslo.vmware-2.26.0/doc/0000775000175100017510000000000013224676314014656 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/doc/source/0000775000175100017510000000000013224676314016156 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/doc/source/user/0000775000175100017510000000000013224676314017134 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/doc/source/user/usage.rst0000666000175100017510000000162413224676076021004 0ustar zuulzuul00000000000000======= Usage ======= Example usage of getting a handle to a vSphere session and retrieving all the ESX hosts in a server:: from oslo_vmware import api from oslo_vmware import vim_util # Get a handle to a vSphere API session session = api.VMwareAPISession( '10.1.2.3', # vSphere host endpoint 'administrator', # vSphere username 'password', # vSphere password 10, # Number of retries for connection failures in tasks 0.1 # Poll interval for async tasks (in seconds) ) # Example call to get all the managed objects of type "HostSystem" # on the server. result = session.invoke_api( vim_util, # Handle to VIM utility module 'get_objects', # API method name to invoke session.vim, 'HostSystem', 100) # Params to API method (*args) oslo.vmware-2.26.0/doc/source/user/history.rst0000666000175100017510000000004013224676076021370 0ustar zuulzuul00000000000000.. include:: ../../../ChangeLog oslo.vmware-2.26.0/doc/source/user/index.rst0000666000175100017510000000015113224676076021001 0ustar zuulzuul00000000000000================= Using oslo.vmware ================= .. toctree:: :maxdepth: 1 usage history oslo.vmware-2.26.0/doc/source/conf.py0000666000175100017510000000627313224676076017474 0ustar zuulzuul00000000000000# 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. import os import sys import fileinput import fnmatch sys.path.insert(0, os.path.abspath('../..')) # -- General configuration ---------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', #'sphinx.ext.intersphinx', 'openstackdocstheme' ] # openstackdocstheme options repository_name = 'openstack/oslo.vmware' bug_project = 'oslo.vmware' bug_tag = '' # autodoc generation is a bit aggressive and a nuisance when doing heavy # text edit cycles. # execute "export SPHINX_DEBUG=1" in your terminal to disable # A list of glob-style patterns that should be excluded when looking for source # files. exclude_patterns = [ 'api/tests.*', # avoid of docs generation from tests 'api/oslo.vmware._*', # skip private modules ] # Prune the excluded patterns from the autoindex PATH = 'api/autoindex.rst' if os.path.isfile(PATH) and os.access(PATH, os.R_OK): for line in fileinput.input(PATH, inplace=True): found = False for pattern in exclude_patterns: if fnmatch.fnmatch(line, '*' + pattern[4:]): found = True if not found: print line, # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = u'oslo.vmware' copyright = u'2014, OpenStack Foundation' # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). add_module_names = True # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # -- Options for HTML output -------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. # html_theme_path = ["."] # html_theme = '_theme' # html_static_path = ['static'] html_theme = 'openstackdocs' html_last_updated_fmt = '%Y-%m-%d %H:%M' # Output file base name for HTML help builder. htmlhelp_basename = '%sdoc' % project # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass # [howto/manual]). latex_documents = [ ('index', '%s.tex' % project, u'%s Documentation' % project, u'OpenStack Foundation', 'manual'), ] # Example configuration for intersphinx: refer to the Python standard library. #intersphinx_mapping = {'http://docs.python.org/': None} oslo.vmware-2.26.0/doc/source/install/0000775000175100017510000000000013224676314017624 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/doc/source/install/index.rst0000666000175100017510000000031213224676076021470 0ustar zuulzuul00000000000000============ Installation ============ At the command line:: $ pip install oslo.vmware Or, if you have virtualenvwrapper installed:: $ mkvirtualenv oslo.vmware $ pip install oslo.vmware oslo.vmware-2.26.0/doc/source/index.rst0000666000175100017510000000064713224676076020035 0ustar zuulzuul00000000000000=================================================== oslo.vmware --- VMware support code for OpenStack =================================================== The Oslo VMware library provides support for common VMware operations and APIs. .. toctree:: :maxdepth: 2 install/index user/index contributor/index reference/index .. rubric:: Indices and tables * :ref:`genindex` * :ref:`modindex` * :ref:`search` oslo.vmware-2.26.0/doc/source/reference/0000775000175100017510000000000013224676314020114 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/doc/source/reference/index.rst0000666000175100017510000000014413224676076021763 0ustar zuulzuul00000000000000============= API Reference ============= .. toctree:: :maxdepth: 1 :glob: api/autoindex oslo.vmware-2.26.0/doc/source/contributor/0000775000175100017510000000000013224676314020530 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/doc/source/contributor/index.rst0000666000175100017510000000004713224676076022401 0ustar zuulzuul00000000000000.. include:: ../../../CONTRIBUTING.rst oslo.vmware-2.26.0/oslo.vmware.egg-info/0000775000175100017510000000000013224676314020057 5ustar zuulzuul00000000000000oslo.vmware-2.26.0/oslo.vmware.egg-info/requires.txt0000664000175100017510000000037413224676313022462 0ustar zuulzuul00000000000000pbr!=2.1.0,>=2.0.0 stevedore>=1.20.0 netaddr>=0.7.18 six>=1.10.0 oslo.i18n>=3.15.3 oslo.utils>=3.33.0 PyYAML>=3.10 lxml!=3.7.0,>=3.4.1 suds-jurko>=0.6 eventlet!=0.18.3,!=0.20.1,<0.21.0,>=0.18.2 requests>=2.14.2 urllib3>=1.21.1 oslo.concurrency>=3.20.0 oslo.vmware-2.26.0/oslo.vmware.egg-info/SOURCES.txt0000664000175100017510000000553513224676314021753 0ustar zuulzuul00000000000000.coveragerc .mailmap .testr.conf AUTHORS CONTRIBUTING.rst ChangeLog HACKING.rst LICENSE README.rst babel.cfg pylintrc requirements.txt setup.cfg setup.py test-requirements.txt tox.ini doc/source/conf.py doc/source/index.rst doc/source/contributor/index.rst doc/source/install/index.rst doc/source/reference/index.rst doc/source/user/history.rst doc/source/user/index.rst doc/source/user/usage.rst oslo.vmware.egg-info/PKG-INFO oslo.vmware.egg-info/SOURCES.txt oslo.vmware.egg-info/dependency_links.txt oslo.vmware.egg-info/not-zip-safe oslo.vmware.egg-info/pbr.json oslo.vmware.egg-info/requires.txt oslo.vmware.egg-info/top_level.txt oslo_vmware/__init__.py oslo_vmware/_i18n.py oslo_vmware/api.py oslo_vmware/constants.py oslo_vmware/exceptions.py oslo_vmware/image_transfer.py oslo_vmware/image_util.py oslo_vmware/pbm.py oslo_vmware/rw_handles.py oslo_vmware/service.py oslo_vmware/version.py oslo_vmware/vim.py oslo_vmware/vim_util.py oslo_vmware/common/__init__.py oslo_vmware/common/loopingcall.py oslo_vmware/hacking/__init__.py oslo_vmware/hacking/checks.py oslo_vmware/locale/en_GB/LC_MESSAGES/oslo_vmware.po oslo_vmware/locale/fr/LC_MESSAGES/oslo_vmware.po oslo_vmware/objects/__init__.py oslo_vmware/objects/datacenter.py oslo_vmware/objects/datastore.py oslo_vmware/tests/__init__.py oslo_vmware/tests/base.py oslo_vmware/tests/test.ovf oslo_vmware/tests/test_api.py oslo_vmware/tests/test_exceptions.py oslo_vmware/tests/test_hacking.py oslo_vmware/tests/test_image_transfer.py oslo_vmware/tests/test_image_util.py oslo_vmware/tests/test_pbm.py oslo_vmware/tests/test_rw_handles.py oslo_vmware/tests/test_service.py oslo_vmware/tests/test_vim.py oslo_vmware/tests/test_vim_util.py oslo_vmware/tests/objects/__init__.py oslo_vmware/tests/objects/test_datacenter.py oslo_vmware/tests/objects/test_datastore.py oslo_vmware/wsdl/5.5/core-types.xsd oslo_vmware/wsdl/5.5/pbm-messagetypes.xsd oslo_vmware/wsdl/5.5/pbm-types.xsd oslo_vmware/wsdl/5.5/pbm.wsdl oslo_vmware/wsdl/5.5/pbmService.wsdl oslo_vmware/wsdl/6.0/core-types.xsd oslo_vmware/wsdl/6.0/pbm-messagetypes.xsd oslo_vmware/wsdl/6.0/pbm-types.xsd oslo_vmware/wsdl/6.0/pbm.wsdl oslo_vmware/wsdl/6.0/pbmService.wsdl oslo_vmware/wsdl/6.5/core-types.xsd oslo_vmware/wsdl/6.5/pbm-messagetypes.xsd oslo_vmware/wsdl/6.5/pbm-types.xsd oslo_vmware/wsdl/6.5/pbm.wsdl oslo_vmware/wsdl/6.5/pbmService.wsdl oslo_vmware/wsdl/6.5/query-types.xsd oslo_vmware/wsdl/6.5/reflect-types.xsd oslo_vmware/wsdl/6.5/vim-types.xsd releasenotes/notes/add_reno-3b4ae0789e9c45b4.yaml releasenotes/source/conf.py releasenotes/source/index.rst releasenotes/source/newton.rst releasenotes/source/ocata.rst releasenotes/source/pike.rst releasenotes/source/unreleased.rst releasenotes/source/_static/.placeholder releasenotes/source/_templates/.placeholder releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po releasenotes/source/locale/fr/LC_MESSAGES/releasenotes.pooslo.vmware-2.26.0/oslo.vmware.egg-info/pbr.json0000664000175100017510000000005613224676313021535 0ustar zuulzuul00000000000000{"git_version": "200706e", "is_release": true}oslo.vmware-2.26.0/oslo.vmware.egg-info/dependency_links.txt0000664000175100017510000000000113224676313024124 0ustar zuulzuul00000000000000 oslo.vmware-2.26.0/oslo.vmware.egg-info/not-zip-safe0000664000175100017510000000000113224676252022306 0ustar zuulzuul00000000000000 oslo.vmware-2.26.0/oslo.vmware.egg-info/PKG-INFO0000664000175100017510000000371513224676313021161 0ustar zuulzuul00000000000000Metadata-Version: 1.1 Name: oslo.vmware Version: 2.26.0 Summary: Oslo VMware library Home-page: https://docs.openstack.org/oslo.vmware/latest/ Author: OpenStack Author-email: openstack-dev@lists.openstack.org License: UNKNOWN Description-Content-Type: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/badges/oslo.vmware.svg :target: https://governance.openstack.org/reference/tags/index.html .. Change things from this point on =================================================== oslo.vmware --- VMware support code for OpenStack =================================================== .. image:: https://img.shields.io/pypi/v/oslo.vmware.svg :target: https://pypi.python.org/pypi/oslo.vmware/ :alt: Latest Version .. image:: https://img.shields.io/pypi/dm/oslo.vmware.svg :target: https://pypi.python.org/pypi/oslo.vmware/ :alt: Downloads The Oslo VMware library provides support for common VMware operations and APIs. * License: Apache License, Version 2.0 * Documentation: https://docs.openstack.org/oslo.vmware/latest/ * Source: https://git.openstack.org/cgit/openstack/oslo.vmware * Bugs: https://bugs.launchpad.net/oslo.vmware Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 oslo.vmware-2.26.0/oslo.vmware.egg-info/top_level.txt0000664000175100017510000000001413224676313022603 0ustar zuulzuul00000000000000oslo_vmware oslo.vmware-2.26.0/setup.cfg0000666000175100017510000000244413224676314015740 0ustar zuulzuul00000000000000[metadata] name = oslo.vmware summary = Oslo VMware library description-file = README.rst author = OpenStack author-email = openstack-dev@lists.openstack.org home-page = https://docs.openstack.org/oslo.vmware/latest/ classifier = Environment :: OpenStack Intended Audience :: Information Technology Intended Audience :: System Administrators License :: OSI Approved :: Apache Software License Operating System :: POSIX :: Linux Programming Language :: Python Programming Language :: Python :: 2 Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 Programming Language :: Python :: 3.5 [files] packages = oslo_vmware [build_sphinx] source-dir = doc/source build-dir = doc/build all_files = 1 warning-is-error = 1 [upload_sphinx] upload-dir = doc/build/html [compile_catalog] directory = oslo_vmware/locale domain = oslo_vmware [update_catalog] domain = oslo_vmware output_dir = oslo_vmware/locale input_file = oslo_vmware/locale/oslo_vmware.pot [extract_messages] keywords = _ gettext ngettext l_ lazy_gettext mapping_file = babel.cfg output_file = oslo_vmware/locale/oslo_vmware.pot [pbr] autodoc_index_modules = 1 autodoc_exclude_modules = oslo_vmware._i18n oslo_vmware.tests.* api_doc_dir = reference/api [wheel] universal = 1 [egg_info] tag_build = tag_date = 0 oslo.vmware-2.26.0/babel.cfg0000666000175100017510000000002013224676076015636 0ustar zuulzuul00000000000000[python: **.py] oslo.vmware-2.26.0/setup.py0000666000175100017510000000200613224676076015630 0ustar zuulzuul00000000000000# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # 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. # THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT import setuptools # In python < 2.7.4, a lazy loading of package `pbr` will break # setuptools if some other modules registered functions in `atexit`. # solution from: http://bugs.python.org/issue15881#msg170215 try: import multiprocessing # noqa except ImportError: pass setuptools.setup( setup_requires=['pbr>=2.0.0'], pbr=True)