cb5fcafe18
The patch addresses 2 things: 1. The deploymen of edges may take longer that the configured timeout. This opertaion now has its own timeout (hardcoded to 20 minutes). 2. The configured timout is now bumoped to double. We have seen that underload there are opertaions that have taken longer than the defult 2 minutes Change-Id: I3ae519b84be58c0f8044fd283aba45f2ed53e431
184 lines
5.8 KiB
Python
184 lines
5.8 KiB
Python
# Copyright 2013 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 base64
|
|
import os
|
|
import xml.etree.ElementTree as et
|
|
|
|
from oslo_context import context as context_utils
|
|
from oslo_serialization import jsonutils
|
|
import requests
|
|
import six
|
|
|
|
from vmware_nsx.plugins.nsx_v.vshield.common import exceptions
|
|
|
|
|
|
def _xmldump(obj):
|
|
"""Sort of improved xml creation method.
|
|
|
|
This converts the dict to xml with following assumptions:
|
|
Keys starting with _(underscore) are to be used as attributes and not
|
|
element keys starting with @ so that dict can be made.
|
|
Keys starting with __(double underscore) are to be skipped and its
|
|
value is processed.
|
|
The keys are not part of any xml schema.
|
|
"""
|
|
|
|
config = ""
|
|
attr = ""
|
|
if isinstance(obj, dict):
|
|
for key, value in six.iteritems(obj):
|
|
if key.startswith('__'):
|
|
# Skip the key and evaluate it's value.
|
|
a, x = _xmldump(value)
|
|
config += x
|
|
elif key.startswith('_'):
|
|
attr += ' %s="%s"' % (key[1:], value)
|
|
else:
|
|
a, x = _xmldump(value)
|
|
if key.startswith('@'):
|
|
cfg = "%s" % (x)
|
|
else:
|
|
cfg = "<%s%s>%s</%s>" % (key, a, x, key)
|
|
|
|
config += cfg
|
|
elif isinstance(obj, list):
|
|
for value in obj:
|
|
a, x = _xmldump(value)
|
|
attr += a
|
|
config += x
|
|
else:
|
|
config = obj
|
|
|
|
return attr, config
|
|
|
|
|
|
def xmldumps(obj):
|
|
attr, xml = _xmldump(obj)
|
|
return xml
|
|
|
|
|
|
class VcnsApiHelper(object):
|
|
errors = {
|
|
303: exceptions.ResourceRedirect,
|
|
400: exceptions.RequestBad,
|
|
403: exceptions.Forbidden,
|
|
404: exceptions.ResourceNotFound,
|
|
409: exceptions.ServiceConflict,
|
|
415: exceptions.MediaTypeUnsupport,
|
|
503: exceptions.ServiceUnavailable
|
|
}
|
|
|
|
nsx_errors = {
|
|
# firewall rule doesn't exists for deletion.
|
|
100046: exceptions.ResourceNotFound,
|
|
100029: exceptions.ResourceNotFound,
|
|
}
|
|
|
|
def __init__(self, address, user, password, format='json', ca_file=None,
|
|
insecure=True, timeout=None):
|
|
self.authToken = base64.encodestring(six.b("%s:%s" % (user, password)))
|
|
self.user = user
|
|
self.passwd = password
|
|
self.address = address
|
|
self.format = format
|
|
self.timeout = timeout
|
|
if format == 'json':
|
|
self.encode = jsonutils.dumps
|
|
else:
|
|
self.encode = xmldumps
|
|
|
|
if insecure:
|
|
self.verify_cert = False
|
|
else:
|
|
if ca_file:
|
|
self.verify_cert = ca_file
|
|
else:
|
|
self.verify_cert = True
|
|
self._session = None
|
|
self._pid = None
|
|
|
|
@property
|
|
def session(self):
|
|
if self._session is None or self._pid != os.getpid():
|
|
self._pid = os.getpid()
|
|
self._session = requests.Session()
|
|
return self._session
|
|
|
|
def _get_nsx_errorcode(self, content):
|
|
try:
|
|
if self.format == 'xml':
|
|
error = et.fromstring(content).find('errorCode')
|
|
errcode = error is not None and int(error.text)
|
|
else: # json
|
|
error = jsonutils.loads(content)
|
|
errcode = int(error.get('errorCode'))
|
|
return errcode
|
|
except (TypeError, ValueError, et.ParseError):
|
|
# We won't assume that integer error-code value is guaranteed.
|
|
return None
|
|
|
|
def _get_request_id(self):
|
|
ctx = context_utils.get_current()
|
|
if ctx:
|
|
return ctx.__dict__.get('request_id')
|
|
|
|
def request(self, method, uri, params=None, headers=None,
|
|
encodeparams=True, timeout=None):
|
|
uri = self.address + uri
|
|
if timeout is None:
|
|
timeout = self.timeout
|
|
if headers is None:
|
|
headers = {}
|
|
|
|
headers['Accept'] = 'application/' + self.format
|
|
headers['Authorization'] = 'Basic ' + self.authToken.strip()
|
|
headers['Content-Type'] = 'application/' + self.format
|
|
request_id = self._get_request_id()
|
|
if request_id:
|
|
headers['TicketNumber'] = request_id
|
|
|
|
if params:
|
|
if encodeparams is True:
|
|
data = self.encode(params)
|
|
else:
|
|
data = params
|
|
else:
|
|
data = None
|
|
|
|
try:
|
|
response = self.session.request(method,
|
|
uri,
|
|
verify=self.verify_cert,
|
|
data=data,
|
|
headers=headers,
|
|
timeout=timeout)
|
|
except requests.exceptions.Timeout:
|
|
raise exceptions.ResourceTimedOut(uri=uri)
|
|
|
|
status = response.status_code
|
|
|
|
if 200 <= status < 300:
|
|
return response.headers, response.text
|
|
|
|
nsx_errcode = self._get_nsx_errorcode(response.text)
|
|
if nsx_errcode in self.nsx_errors:
|
|
cls = self.nsx_errors[nsx_errcode]
|
|
elif status in self.errors:
|
|
cls = self.errors[status]
|
|
else:
|
|
cls = exceptions.VcnsApiException
|
|
raise cls(uri=uri, status=status,
|
|
header=response.headers, response=response.text)
|