vmware-nsx/vmware_nsx/plugins/nsx_v/vshield/common/VcnsApiClient.py
Michal Kelner Mishali d50175fd6a NSX-V: decode authToken for supporting python 3.5
When running devstack on Ubuntu 16 and py35 set
as default, Neutron failed to start due to error
"Can't convert 'bytes' object to str implicitly"

Change-Id: I454d5d7845cd60b2f11028cafab28acdcc737c6d
Signed-off-by: Michal Kelner Mishali <mkelnermishal@vmware.com>
2018-05-22 14:13:28 +03:00

185 lines
5.9 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 = {}
auth_token = self.authToken.decode('ascii').strip()
headers['Accept'] = 'application/' + self.format
headers['Authorization'] = 'Basic ' + auth_token
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)