VMware storage backend should use oslo.vmware

Currently, the VMware store is using its own copy of the
VMwareApiSession to connect to vCenter server and ESX(i).
This patch gets rid of this copy to use the oslo.vmware library.

Closes-Bug: #1282715

Change-Id: I0aa47eada388c09d9835b00fb2c93f50f22675a4
This commit is contained in:
Arnaud Legendre 2014-02-25 14:48:11 -08:00
parent 3d5202df45
commit 18b4df178b
9 changed files with 16 additions and 869 deletions

View File

@ -1,273 +0,0 @@
# 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.
Provides abstraction over glance.vmware.vim.Vim SOAP calls.
"""
from eventlet import event
import glance.openstack.common.log as logging
from glance.openstack.common import loopingcall
from glance.store.vmware import error_util
from glance.store.vmware import vim
from glance.store.vmware import vim_util
LOG = logging.getLogger(__name__)
class Retry(object):
"""Decorator for retrying a function upon suggested exceptions.
The method retries for given number of times and the sleep
time increments till the max sleep time is reached.
If max retries is set to -1, then the decorated function is
invoked indefinitely till no exception is thrown or if
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=()):
"""Initialize retry object based on input params.
:param max_retry_count: Max number of times, a function must be
retried when one of input 'exceptions'
is caught. The default -1 will always
retry the function till a non-exception
case, or an un-wanted error case arises.
:param inc_sleep_time: Incremental time in seconds for sleep time
between retrial
:param max_sleep_time: Max sleep time beyond which the sleep time will
not be incremented using param inc_sleep_time
and max_sleep_time will be used as 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):
def _func(done, *args, **kwargs):
try:
result = f(*args, **kwargs)
done.send(result)
except self._exceptions as excep:
LOG.exception(_("Failure while invoking function: "
"%(func)s. Error: %(excep)s.") %
{'func': f.__name__, 'excep': excep})
if (self._max_retry_count != -1 and
self._retry_count >= self._max_retry_count):
done.send_exception(excep)
else:
self._retry_count += 1
self._sleep_time += self._inc_sleep_time
return self._sleep_time
except Exception as excep:
done.send_exception(excep)
return 0
def func(*args, **kwargs):
done = event.Event()
loop = loopingcall.DynamicLoopingCall(_func, done, *args, **kwargs)
loop.start(periodic_interval_max=self._max_sleep_time)
result = done.wait()
loop.stop()
return result
return func
class VMwareAPISession(object):
"""Sets up a session with the server and handles all calls made to it."""
def __init__(self, server_ip, server_username, server_password,
api_retry_count, task_poll_interval=5.0,
scheme='https', create_session=True,
wsdl_loc=None):
"""Constructs session object.
:param server_ip: IP address of ESX/VC server
: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 scheme: http or https protocol
:param create_session: Boolean whether to set up connection at the
time of instance creation
:param wsdl_loc: WSDL file location for invoking SOAP calls on server
using suds
"""
self._server_ip = server_ip
self._server_username = server_username
self._server_password = server_password
self._wsdl_loc = wsdl_loc
self._api_retry_count = api_retry_count
self._task_poll_interval = task_poll_interval
self._scheme = scheme
self._session_id = None
self._vim = None
if create_session:
self.create_session()
@property
def vim(self):
if not self._vim:
self._vim = vim.Vim(protocol=self._scheme, host=self._server_ip,
wsdl_loc=self._wsdl_loc)
return self._vim
@Retry(exceptions=(Exception))
def create_session(self):
"""Establish session with the server."""
# Login and setup the session with the server for making
# API calls
session_manager = self.vim.service_content.sessionManager
session = self.vim.Login(session_manager,
userName=self._server_username,
password=self._server_password)
# Terminate the earlier session, if possible (For the sake of
# preserving sessions as there is a limit to the number of
# sessions we can have)
if self._session_id:
try:
self.vim.TerminateSession(session_manager,
sessionId=[self._session_id])
except Exception as excep:
# This exception is something we can live with. It is
# just an extra caution on our side. The session may
# have been cleared. We could have made a call to
# SessionIsActive, but that is an overhead because we
# anyway would have to call TerminateSession.
LOG.exception(_("Error while terminating session: %s.") %
excep)
self._session_id = session.key
LOG.info(_("Successfully established connection to the server."))
def __del__(self):
"""Logs-out the session."""
try:
self.vim.Logout(self.vim.service_content.sessionManager)
except Exception as excep:
LOG.exception(_("Error while logging out the user: %s.") %
excep)
def invoke_api(self, module, method, *args, **kwargs):
"""Wrapper method for invoking APIs.
Here we retry the API calls for exceptions which may come because
of session overload.
Make sure if a Vim instance is being passed here, this session's
Vim (self.vim) instance is used, as we retry establishing session
in case of session timedout.
:param module: Module invoking the VI SDK calls
:param method: Method in the module that invokes the VI SDK call
:param args: Arguments to the method
:param kwargs: Keyword arguments to the method
:return: Response of the API call
"""
@Retry(max_retry_count=self._api_retry_count,
exceptions=(error_util.VimException))
def _invoke_api(module, method, *args, **kwargs):
last_fault_list = []
while True:
try:
api_method = getattr(module, method)
return api_method(*args, **kwargs)
except error_util.VimFaultException as excep:
if error_util.NOT_AUTHENTICATED not in excep.fault_list:
raise excep
# If it is a not-authenticated fault, we re-authenticate
# the user and retry the API invocation.
# Because of the idle session returning an empty
# RetrieveProperties response and also the same is
# returned when there is an empty answer to a query
# (e.g. no VMs on the host), we have no way to
# differentiate.
# So if the previous response was also an empty
# response and after creating a new session, we get
# the same empty response, then we are sure of the
# response being an empty response.
if error_util.NOT_AUTHENTICATED in last_fault_list:
return []
last_fault_list = excep.fault_list
LOG.warn(_("Not authenticated error occurred. "
"Will create session and try "
"API call again: %s.") % excep)
self.create_session()
return _invoke_api(module, method, *args, **kwargs)
def _stop_loop(self, loop):
loop.stop()
def wait_for_task(self, task):
"""Return a deferred that will give the result of the given task.
The task is polled until it completes. The method returns the task
information upon successful completion.
:param task: Managed object reference of the task
:return: Task info upon successful completion of the task
"""
done = event.Event()
loop = loopingcall.FixedIntervalLoopingCall(self._poll_task,
task, done)
loop.start(self._task_poll_interval)
task_info = done.wait()
loop.stop()
return task_info
def _poll_task(self, task, done):
"""Poll the given task.
If the task completes successfully then returns task info.
In case of error sends back appropriate error.
:param task: Managed object reference of the task
:param done: Event that captures task status
"""
try:
task_info = self.invoke_api(vim_util, 'get_object_property',
self.vim, task, 'info')
if task_info.state in ['queued', 'running']:
# If task already completed on server, it will not return
# the progress.
if hasattr(task_info, 'progress'):
LOG.debug(_("Task: %(task)s progress: %(prog)s.") %
{'task': task, 'prog': task_info.progress})
return
elif task_info.state == 'success':
LOG.debug(_("Task %s status: success.") % task)
done.send(task_info)
else:
error_msg = str(task_info.error.localizedMessage)
LOG.exception(_("Task: %(task)s failed with error: %(err)s.") %
{'task': task, 'err': error_msg})
done.send_exception(error_util.VimFaultException([],
error_msg))
except Exception as excep:
LOG.exception(_("Task: %(task)s failed with error: %(err)s.") %
{'task': task, 'err': excep})
done.send_exception(excep)

View File

@ -1,48 +0,0 @@
# 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 classes and SOAP response error checking module.
"""
from glance.common import exception
NOT_AUTHENTICATED = 'NotAuthenticated'
class VimException(exception.GlanceException):
"""The VIM Exception class."""
def __init__(self, msg):
exception.GlanceException.__init__(self, msg)
class SessionOverLoadException(VimException):
"""Session Overload Exception."""
pass
class VimAttributeException(VimException):
"""VI Attribute Error."""
pass
class VimFaultException(VimException):
"""The VIM Fault exception class."""
def __init__(self, fault_list, msg):
super(VimFaultException, self).__init__(msg)
self.fault_list = fault_list

View File

@ -1,241 +0,0 @@
# 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 for making VMware VI SOAP calls.
"""
import httplib
import logging
import suds
from glance.store.vmware import error_util
logging.getLogger('suds').setLevel(logging.INFO)
RESP_NOT_XML_ERROR = "Response is 'text/html', not 'text/xml'"
CONN_ABORT_ERROR = 'Software caused connection abort'
ADDRESS_IN_USE_ERROR = 'Address already in use'
def get_moref(value, type):
"""Get managed object reference.
:param value: value for the managed object
:param type: type of the managed object
:return: Managed object reference with with input value and type
"""
moref = suds.sudsobject.Property(value)
moref._type = type
return moref
class VIMMessagePlugin(suds.plugin.MessagePlugin):
def addAttributeForValue(self, node):
"""Helper to handle AnyType.
suds does not handle AnyType properly.
VI SDK requires type attribute to be set when AnyType is used
:param node: XML value node
"""
if node.name == 'value':
node.set('xsi:type', 'xsd:string')
def marshalled(self, context):
"""Marshal soap context.
Provides the plugin with the opportunity to prune empty
nodes and fixup nodes before sending it to the server.
:param context: SOAP 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. <test/> as opposed to <test>test</test>
context.envelope.prune()
context.envelope.walk(self.addAttributeForValue)
class Vim(object):
"""The VIM Object."""
def __init__(self, protocol='https', host='localhost', wsdl_loc=None):
"""Create communication interfaces for initiating SOAP transactions.
:param protocol: http or https
:param host: Server IPAddress[:port] or Hostname[:port]
:param wsdl_loc: Optional location of the VIM WSDL
"""
self._protocol = protocol
self._host_name = host
if not wsdl_loc:
wsdl_loc = Vim._get_wsdl_loc(protocol, host)
soap_url = Vim._get_soap_url(protocol, host)
self._client = suds.client.Client(wsdl_loc, location=soap_url,
plugins=[VIMMessagePlugin()])
self._service_content = self.RetrieveServiceContent('ServiceInstance')
@staticmethod
def _get_wsdl_loc(protocol, host_name):
"""Return default WSDL file location hosted at the server.
:param protocol: http or https
:param host_name: ESX/VC server host name
:return: Default WSDL file location hosted at the server
"""
return '%s://%s/sdk/vimService.wsdl' % (protocol, host_name)
@staticmethod
def _get_soap_url(protocol, host_name):
"""Return URL to SOAP services for ESX/VC server.
:param protocol: https or http
:param host_name: ESX/VC server host name
:return: URL to SOAP services for ESX/VC server
"""
return '%s://%s/sdk' % (protocol, host_name)
@property
def service_content(self):
return self._service_content
@property
def client(self):
return self._client
def __getattr__(self, attr_name):
"""Makes the API call and gets the result."""
def retrieve_properties_ex_fault_checker(response):
"""Checks the RetrievePropertiesEx response for errors.
Certain faults are sent as part of the SOAP body as property of
missingSet. For example NotAuthenticated fault. The method raises
appropriate VimFaultException when an error is found.
:param response: Response from RetrievePropertiesEx API call
"""
fault_list = []
if not response:
# This is the case when the session has timed out. ESX SOAP
# server sends an empty RetrievePropertiesExResponse. Normally
# missingSet in the returnval field 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. So setting fault to NotAuthenticated fault.
fault_list = [error_util.NOT_AUTHENTICATED]
else:
for obj_cont in response:
if hasattr(obj_cont, 'missingSet'):
for missing_elem in obj_cont.missingSet:
fault_type = missing_elem.fault.fault.__class__
# Fault needs to be added to the type of fault
# for uniformity in error checking as SOAP faults
# define
fault_list.append(fault_type.__name__)
if fault_list:
exc_msg_list = ', '.join(fault_list)
raise error_util.VimFaultException(fault_list,
_("Error(s): %s occurred "
"in the call to "
"RetrievePropertiesEx.") %
exc_msg_list)
def vim_request_handler(managed_object, **kwargs):
"""Handler for VI SDK calls.
Builds the SOAP message and parses the response for fault
checking and other errors.
:param managed_object:Managed object reference
:param kwargs: Keyword arguments of the call
:return: Response of the API call
"""
try:
if isinstance(managed_object, str):
# For strings use string value for value and type
# of the managed object.
managed_object = get_moref(managed_object, managed_object)
request = getattr(self.client.service, attr_name)
response = request(managed_object, **kwargs)
if (attr_name.lower() == 'retrievepropertiesex'):
retrieve_properties_ex_fault_checker(response)
return response
except error_util.VimFaultException as excep:
raise
except suds.WebFault as excep:
doc = excep.document
detail = doc.childAtPath('/Envelope/Body/Fault/detail')
fault_list = []
for child in detail.getChildren():
fault_list.append(child.get('type'))
raise error_util.VimFaultException(fault_list, str(excep))
except AttributeError as excep:
raise error_util.VimAttributeException(_("No such SOAP method "
"%(attr)s. Detailed "
"error: %(excep)s.") %
{'attr': attr_name,
'excep': excep})
except (httplib.CannotSendRequest,
httplib.ResponseNotReady,
httplib.CannotSendHeader) as excep:
raise error_util.SessionOverLoadException(_("httplib error in "
"%(attr)s: "
"%(excep)s.") %
{'attr': attr_name,
'excep': excep})
except Exception as excep:
# Socket errors which need special handling for they
# might be caused by server API call overload
if (str(excep).find(ADDRESS_IN_USE_ERROR) != -1 or
str(excep).find(CONN_ABORT_ERROR)) != -1:
raise error_util.SessionOverLoadException(_("Socket error "
"in %(attr)s: "
"%(excep)s.") %
{'attr':
attr_name,
'excep': excep})
# Type error that needs special handling for it might be
# caused by server API call overload
elif str(excep).find(RESP_NOT_XML_ERROR) != -1:
raise error_util.SessionOverLoadException(_("Type error "
"in %(attr)s: "
"%(excep)s.") %
{'attr':
attr_name,
'excep': excep})
else:
raise error_util.VimException(_("Error in %(attr)s. "
"Detailed error: "
"%(excep)s.") %
{'attr': attr_name,
'excep': excep})
return vim_request_handler
def __repr__(self):
return "VIM Object."
def __str__(self):
return "VIM Object."

View File

@ -1,301 +0,0 @@
# 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.
"""
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
:return: 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 object.
:param client_factory: Factory to get API input specs
:param name: Name for the traversal spec
:param type: Type of the managed object reference
:param path: Property path of the managed object reference
: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
:return: 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
:return: 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])
# 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,
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 reference property
:param properties_to_collect: Properties of the managed object reference
to be collected while traversal filtering
:param all_properties: Whether all the properties of managed object
reference needs to be collected
:return: 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 as the starting point for
traversal
:param traversal_specs: filter specs required for traversal
:return: 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
:return: 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, props_to_collect=None,
all_properties=False):
"""Gets all managed object references of a specified type.
It is caller's responsibility to continue or cancel retrieval.
:param vim: Vim object
:param type: Type of the managed object reference
:param max_objects: Maximum number of objects that should be returned in
a single call
:param props_to_collect: Properties of the managed object reference
to be collected
:param all_properties: Whether all properties of the managed object
reference are to be collected
:return: All managed object references of a specified type
"""
if not props_to_collect:
props_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=props_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, mobj, properties):
"""Gets properties of the managed object specified.
:param vim: Vim object
:param mobj: Reference to the managed object
:param properties: Properties of the managed object reference
to be retrieved
:return: Properties of the managed object specified
"""
client_factory = vim.client.factory
if mobj is None:
return None
collector = vim.service_content.propertyCollector
property_filter_spec = client_factory.create('ns0:PropertyFilterSpec')
property_spec = client_factory.create('ns0:PropertySpec')
property_spec.all = (properties is None or len(properties) == 0)
property_spec.pathSet = properties
property_spec.type = mobj._type
object_spec = client_factory.create('ns0:ObjectSpec')
object_spec.obj = mobj
object_spec.skip = False
property_filter_spec.propSet = [property_spec]
property_filter_spec.objectSet = [object_spec]
options = client_factory.create('ns0:RetrieveOptions')
options.maxObjects = 1
retrieve_result = vim.RetrievePropertiesEx(collector,
specSet=[property_filter_spec],
options=options)
cancel_retrieval(vim, retrieve_result)
return retrieve_result.objects
def _get_token(retrieve_result):
"""Get token from results to obtain next set of results.
:retrieve_result: Result from the RetrievePropertiesEx API
:return: 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 from the RetrievePropertiesEx API
"""
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 present.
:param vim: Vim object
:param retrieve_result: Result from the RetrievePropertiesEx API
"""
token = _get_token(retrieve_result)
if token:
collector = vim.service_content.propertyCollector
return vim.ContinueRetrievePropertiesEx(collector, token=token)
def get_object_property(vim, mobj, property_name):
"""Gets property of the managed object specified.
:param vim: Vim object
:param mobj: Reference to the managed object
:param property_name: Name of the property to be retrieved
:return: Property of the managed object specified
"""
props = get_object_properties(vim, mobj, [property_name])
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

View File

@ -20,6 +20,7 @@ import httplib
import netaddr
from oslo.config import cfg
from oslo.vmware import api
import six.moves.urllib.parse as urlparse
from glance.common import exception
@ -27,7 +28,7 @@ import glance.openstack.common.log as logging
import glance.store
import glance.store.base
import glance.store.location
from glance.store.vmware import api
LOG = logging.getLogger(__name__)

View File

@ -23,17 +23,21 @@ VMware Datastore backend
import ConfigParser
import httplib
import logging
import os
import oslo.config.cfg
from oslo.vmware import api
import six.moves.urllib.parse as urlparse
import testtools
from glance.store.vmware import api
import glance.store.vmware_datastore
import glance.tests.functional.store as store_tests
logging.getLogger('suds').setLevel(logging.INFO)
def read_config(path):
cp = ConfigParser.RawConfigParser()
cp.read(path)
@ -47,6 +51,7 @@ def parse_config(config):
'vmware_server_username',
'vmware_server_password',
'vmware_api_retry_count',
'vmware_task_poll_interval',
'vmware_store_image_dir',
'vmware_datacenter_path',
'vmware_datastore_name',
@ -63,13 +68,14 @@ class VMwareDatastoreStoreError(RuntimeError):
def vsphere_connect(server_ip, server_username, server_password,
api_retry_count, scheme='https',
create_session=True, wsdl_loc=None):
api_retry_count, task_poll_interval,
scheme='https', create_session=True, wsdl_loc=None):
try:
return api.VMwareAPISession(server_ip,
server_username,
server_password,
api_retry_count,
task_poll_interval,
scheme=scheme,
create_session=create_session,
wsdl_loc=wsdl_loc)
@ -105,6 +111,7 @@ class TestVMwareDatastoreStore(store_tests.BaseTestCase, testtools.TestCase):
config['vmware_server_username'],
config['vmware_server_password'],
config['vmware_api_retry_count'],
config['vmware_task_poll_interval'],
scheme=scheme)
self.vmware_config = config

View File

@ -78,7 +78,7 @@ class FakeHTTPConnection(object):
class TestStore(base.StoreClearingUnitTest):
@mock.patch('glance.store.vmware.api.VMwareAPISession', autospec=True)
@mock.patch('oslo.vmware.api.VMwareAPISession', autospec=True)
def setUp(self, mock_session):
"""Establish a clean test environment"""
super(TestStore, self).setUp()

View File

@ -22,11 +22,13 @@ iso8601>=0.1.8
ordereddict
oslo.config>=1.2.0
stevedore>=0.14
suds>=0.4
# For Swift storage backend.
python-swiftclient>=1.6
# For VMware storage backed.
oslo.vmware
# For paste.util.template used in keystone.common.template
Paste