Skip to content

Commit

Permalink
NetApp server tunneling fix.
Browse files Browse the repository at this point in the history
NetApp vserver/vfiler api tunneling in concurrent requests
can cause routing request to incorrect servers in race conditions.
This fixes the api and driver for the mentioned situations.

bug 1176759

Change-Id: Id283822e4d1553daed4e6d690ec36f2edfbcf2b4
  • Loading branch information
singn committed May 15, 2013
1 parent 180a320 commit 837df12
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 122 deletions.
70 changes: 41 additions & 29 deletions cinder/volume/drivers/netapp/api.py
Expand Up @@ -30,7 +30,7 @@


class NaServer(object):
""" Encapsulates server connection logic"""
"""Encapsulates server connection logic."""

TRANSPORT_TYPE_HTTP = 'http'
TRANSPORT_TYPE_HTTPS = 'https'
Expand Down Expand Up @@ -60,7 +60,8 @@ def get_transport_type(self):

def set_transport_type(self, transport_type):
"""Set the transport type protocol for api.
Supports http and https transport types.
Supports http and https transport types.
"""
if transport_type.lower() not in (
NaServer.TRANSPORT_TYPE_HTTP,
Expand All @@ -85,8 +86,8 @@ def get_style(self):

def set_style(self, style):
"""Set the authorization style for communicating with the server.
Supports basic_auth for now.
Certificate_auth mode to be done.
Supports basic_auth for now. Certificate_auth mode to be done.
"""
if style.lower() not in (NaServer.STYLE_LOGIN_PASSWORD,
NaServer.STYLE_CERTIFICATE):
Expand All @@ -99,7 +100,8 @@ def get_server_type(self):

def set_server_type(self, server_type):
"""Set the target server type.
Supports filer and dfm server types.
Supports filer and dfm server types.
"""
if server_type.lower() not in (NaServer.SERVER_TYPE_FILER,
NaServer.SERVER_TYPE_DFM):
Expand Down Expand Up @@ -142,7 +144,7 @@ def get_port(self):
return self._port

def set_timeout(self, seconds):
"""Sets the timeout in seconds"""
"""Sets the timeout in seconds."""
try:
self._timeout = int(seconds)
except ValueError:
Expand All @@ -155,23 +157,23 @@ def get_timeout(self):
return None

def get_vfiler(self):
"""Get the vfiler tunneling."""
"""Get the vfiler to use in tunneling."""
return self._vfiler

def set_vfiler(self, vfiler):
"""Set the vfiler tunneling."""
"""Set the vfiler to use if tunneling gets enabled."""
self._vfiler = vfiler

def get_vserver(self):
"""Get the vserver for tunneling."""
"""Get the vserver to use in tunneling."""
return self._vserver

def set_vserver(self, vserver):
"""Set the vserver for tunneling."""
"""Set the vserver to use if tunneling gets enabled."""
self._vserver = vserver

def set_username(self, username):
"""Set the username for authentication."""
"""Set the user name for authentication."""
self._username = username
self._refresh_conn = True

Expand All @@ -180,11 +182,11 @@ def set_password(self, password):
self._password = password
self._refresh_conn = True

def invoke_elem(self, na_element):
def invoke_elem(self, na_element, enable_tunneling=False):
"""Invoke the api on the server."""
if na_element and not isinstance(na_element, NaElement):
ValueError('NaElement must be supplied to invoke api')
request = self._create_request(na_element)
request = self._create_request(na_element, enable_tunneling)
if not hasattr(self, '_opener') or not self._opener \
or self._refresh_conn:
self._build_opener()
Expand All @@ -200,9 +202,15 @@ def invoke_elem(self, na_element):
xml = response.read()
return self._get_result(xml)

def invoke_successfully(self, na_element):
"""Invokes api and checks execution status as success."""
result = self.invoke_elem(na_element)
def invoke_successfully(self, na_element, enable_tunneling=False):
"""Invokes api and checks execution status as success.
Need to set enable_tunneling to True explicitly to achieve it.
This helps to use same connection instance to enable or disable
tunneling. The vserver or vfiler should be set before this call
otherwise tunneling remains disabled.
"""
result = self.invoke_elem(na_element, enable_tunneling)
if result.has_attr('status') and result.get_attr('status') == 'passed':
return result
code = result.get_attr('errno')\
Expand All @@ -213,12 +221,23 @@ def invoke_successfully(self, na_element):
or 'Execution status is failed due to unknown reason'
raise NaApiError(code, msg)

def _create_request(self, na_element):
def _create_request(self, na_element, enable_tunneling=False):
"""Creates request in the desired format."""
netapp_elem = NaElement('netapp')
netapp_elem.add_attr('xmlns', self._ns)
if hasattr(self, '_api_version'):
netapp_elem.add_attr('version', self._api_version)
if enable_tunneling:
self._enable_tunnel_request(netapp_elem)
netapp_elem.add_child_elem(na_element)
request_d = netapp_elem.to_string()
request = urllib2.Request(
self._get_url(), data=request_d,
headers={'Content-Type': 'text/xml', 'charset': 'utf-8'})
return request

def _enable_tunnel_request(self, netapp_elem):
"""Enables vserver or vfiler tunneling."""
if hasattr(self, '_vfiler') and self._vfiler:
if hasattr(self, '_api_major_version') and \
hasattr(self, '_api_minor_version') and \
Expand All @@ -237,12 +256,6 @@ def _create_request(self, na_element):
else:
raise ValueError('ontapi version has to be atleast 1.15'
' to send request to vserver')
netapp_elem.add_child_elem(na_element)
request_d = netapp_elem.to_string()
request = urllib2.Request(
self._get_url(), data=request_d,
headers={'Content-Type': 'text/xml', 'charset': 'utf-8'})
return request

def _parse_response(self, response):
"""Get the NaElement for the response."""
Expand Down Expand Up @@ -352,8 +365,8 @@ def get_attr_names(self):

def add_new_child(self, name, content, convert=False):
"""Add child with tag name and context.
Convert replaces entity refs to chars.
"""
Convert replaces entity refs to chars."""
child = NaElement(name)
if convert:
content = NaElement._convert_entity_refs(content)
Expand All @@ -362,9 +375,7 @@ def add_new_child(self, name, content, convert=False):

@staticmethod
def _convert_entity_refs(text):
"""Converts entity refs to chars
neccessary to handle etree auto conversions.
"""
"""Converts entity refs to chars to handle etree auto conversions."""
text = text.replace("&lt;", "<")
text = text.replace("&gt;", ">")
return text
Expand All @@ -383,13 +394,14 @@ def add_node_with_children(self, node, **children):
self.add_child_elem(parent)

def to_string(self, pretty=False, method='xml', encoding='UTF-8'):
"""Prints the element to string"""
"""Prints the element to string."""
return etree.tostring(self._element, method=method, encoding=encoding,
pretty_print=pretty)


class NaApiError(Exception):
"""Base exception class for NetApp api errors."""

def __init__(self, code='unknown', message='unknown'):
self.code = code
self.message = message
Expand Down

0 comments on commit 837df12

Please sign in to comment.