Download ISO in more simple way.

In order to add support of transferring an ISO from one datastore
to another, below changes are done:
- Return and close connection for read_connection
- Add FileReadHandle similar to FileWriteHandle
- Add common headers to _create_connection
- Accept pre-build cookie for FileReadHandle/FileWriteHandle
- DatastoreUrl as parameter for FileReadHandle/FileWriteHandle

Closes-bug: #1975618
Change-Id: I311c98201e3a89db561b7a0c64592803b32a8b31
This commit is contained in:
Fabian Wiesel 2022-05-24 19:45:15 +00:00 committed by Kiran Pawar
parent 54a96f50c8
commit 42ca5ddb26
1 changed files with 108 additions and 29 deletions

View File

@ -60,7 +60,7 @@ class FileHandle(object):
self._file_handle = file_handle
def _create_connection(self, url, method, cacerts=False,
ssl_thumbprint=None):
ssl_thumbprint=None, cookies=None):
_urlparse = urlparse.urlparse(url)
scheme, netloc, path, params, query, fragment = _urlparse
if scheme == 'http':
@ -88,18 +88,20 @@ class FileHandle(object):
if query:
path = path + '?' + query
conn.putrequest(method, path)
conn.putheader('User-Agent', USER_AGENT)
if cookies:
vim_cookie = self._build_vim_cookie_header(cookies)
conn.putheader('Cookie', vim_cookie)
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 = self._create_connection(url, 'GET', cacerts, ssl_thumbprint,
cookies=cookies)
conn.endheaders()
return conn.getresponse()
return conn
except Exception as excep:
# TODO(vbala) We need to catch and raise specific exceptions
# related to connection problems, invalid request and invalid
@ -123,19 +125,13 @@ class FileHandle(object):
'url': url})
try:
conn = self._create_connection(url, method, cacerts,
ssl_thumbprint)
headers = {'User-Agent': USER_AGENT}
ssl_thumbprint, cookies=cookies)
if file_size:
headers.update({'Content-Length': str(file_size)})
conn.putheader('Content-Length', str(file_size))
if overwrite:
headers.update({'Overwrite': overwrite})
if cookies:
headers.update({'Cookie':
self._build_vim_cookie_header(cookies)})
conn.putheader('Overwrite', overwrite)
if content_type:
headers.update({'Content-Type': content_type})
for key, value in headers.items():
conn.putheader(key, value)
conn.putheader('Content-Type', content_type)
conn.endheaders()
return conn
except requests.RequestException as excep:
@ -154,11 +150,12 @@ class FileHandle(object):
def _build_vim_cookie_header(self, vim_cookies):
"""Build ESX host session cookie header."""
cookie_header = ""
# As returned from DatastoreURL.get_transfer_ticket
if isinstance(vim_cookies, str):
return vim_cookies
for vim_cookie in vim_cookies:
cookie_header = vim_cookie.name + '=' + vim_cookie.value
break
return cookie_header
return vim_cookie.name + '=' + vim_cookie.value
return ""
def write(self, data):
"""Write data to the file.
@ -219,17 +216,21 @@ class FileHandle(object):
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,
def __init__(self, host_or_url, port=None, data_center_name=None,
datastore_name=None, cookies=None, file_path=None,
file_size=None, scheme='https', cacerts=False,
thumbprint=None):
"""Initializes the write handle with given parameters.
:param host: ESX/VC server IP address or host name
:param host_or_url: ESX/VC server IP address or host name or a complete
DatastoreURL
: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 cookies: cookies to build the vim cookie header, or a string
with the prebuild vim cookie header
(See: DatastoreURL.get_transfer_ticket())
: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
@ -237,10 +238,13 @@ class FileWriteHandle(FileHandle):
: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)
if not port and not data_center_name and not datastore_name:
self._url = host_or_url
else:
soap_url = self._get_soap_url(scheme, host_or_url, 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,
@ -286,6 +290,79 @@ class FileWriteHandle(FileHandle):
return "File write handle for %s" % self._url
class FileReadHandle(FileHandle):
"""Read handle for a file in VMware server."""
def __init__(self, host_or_url, port=None, data_center_name=None,
datastore_name=None, cookies=None,
file_path=None, scheme='https', cacerts=False,
thumbprint=None):
"""Initializes the read handle with given parameters.
:param host_or_url: ESX/VC server IP address or host name or a complete
DatastoreURL
: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, or a string
with the prebuild vim cookie header
(See: DatastoreURL.get_transfer_ticket())
:param file_path: datastore path where the file is written
: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
"""
if not port and not data_center_name and not datastore_name:
self._url = host_or_url
else:
soap_url = self._get_soap_url(scheme, host_or_url, 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_read_connection(self._url,
cookies=cookies,
cacerts=cacerts,
ssl_thumbprint=thumbprint)
FileHandle.__init__(self, self._conn.getresponse())
def read(self, length):
"""Read data from the file.
:param length: amount of data to be read
:raises: VimConnectionException, VimException
"""
try:
return self._file_handle.read(length)
except requests.RequestException as excep:
excep_msg = _("Connection error occurred while reading data from"
" %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):
"""Closes the connection.
"""
self._conn.close()
super(FileReadHandle, self).close()
LOG.debug("Closed File read handle for %s.", self._url)
def get_size(self):
return self._file_handle.getheader('Content-Length')
def __str__(self):
return "File read handle for %s" % self._url
class VmdkHandle(FileHandle):
"""VMDK handle based on HttpNfcLease."""
@ -608,7 +685,8 @@ class VmdkReadHandle(VmdkHandle):
self._conn = self._create_read_connection(url,
cookies=cookies,
ssl_thumbprint=thumbprint)
super(VmdkReadHandle, self).__init__(session, lease, url, self._conn)
super(VmdkReadHandle, self).__init__(session, lease, url,
self._conn.getresponse())
def read(self, chunk_size=READ_CHUNKSIZE):
"""Read a chunk of data from the VMDK file.
@ -639,6 +717,7 @@ class VmdkReadHandle(VmdkHandle):
:raises: VimException, VimFaultException, VimAttributeException,
VimSessionOverLoadException, VimConnectionException
"""
self._conn.close()
try:
self._release_lease()
except exceptions.ManagedObjectNotFoundException: