Huang Rui cf77385929 The init commmit of stackforge/zvm-driver
Including all python modules for nova-zvm-virt-driver and
neutron-zvm-plugin.

Change-Id: I72dd9f64fc412cbf10f5e7ab6e4ac465a977e849
2014-11-04 17:03:02 +08:00

206 lines
6.8 KiB
Python
Executable File

# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2014 IBM Corp.
#
# 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 functools
import httplib
from neutron.openstack.common import jsonutils
from neutron.openstack.common.gettextutils import _
from neutron.openstack.common import log as logging
from neutron.plugins.zvm.common import config
from neutron.plugins.zvm.common import constants
from neutron.plugins.zvm.common import exception
LOG = logging.getLogger(__name__)
CONF = config.CONF
class xCatURL(object):
"""To return xCat url for invoking xCat REST API."""
def __init__(self):
"""Set constant that used to form xCat url."""
self.PREFIX = '/xcatws'
self.SUFFIX = '?userName=' + CONF.AGENT.zvm_xcat_username + \
'&password=' + CONF.AGENT.zvm_xcat_password + \
'&format=json'
self.NODES = '/nodes'
self.TABLES = '/tables'
self.XDSH = '/dsh'
def tabdump(self, arg='', addp=None):
rurl = self.PREFIX + self.TABLES + arg + self.SUFFIX
return self._append_addp(rurl, addp)
def _append_addp(self, rurl, addp=None):
if addp is not None:
return rurl + addp
else:
return rurl
def gettab(self, arg='', addp=None):
"""Get table arg, with attribute addp."""
rurl = self.PREFIX + self.TABLES + arg + self.SUFFIX
return self._append_addp(rurl, addp)
def tabch(self, arg='', addp=None):
"""Add/update/delete row(s) in table arg, with attribute addp."""
rurl = self.PREFIX + self.TABLES + arg + self.SUFFIX
return self._append_addp(rurl, addp)
def xdsh(self, arg=''):
"""Run shell command."""
return self.PREFIX + self.NODES + arg + self.XDSH + self.SUFFIX
class xCatConnection():
"""Https requests to xCat web service."""
def __init__(self):
"""Initialize https connection to xCat service."""
self.host = CONF.AGENT.zvm_xcat_server
self.xcat_timeout = CONF.AGENT.zvm_xcat_timeout
try:
self.conn = httplib.HTTPSConnection(self.host, None, None, None,
True, self.xcat_timeout)
except Exception:
LOG.error(_("Connect to xCat server %s failed") % self.host)
raise exception.zVMxCatConnectionFailed(xcatserver=self.host)
def request(self, method, url, body=None, headers={}):
"""Do http request to xCat server
Will return (response_status, response_reason, response_body)
"""
if body is not None:
body = jsonutils.dumps(body)
headers = {'content-type': 'text/plain',
'content-length': len(body)}
try:
self.conn.request(method, url, body, headers)
except Exception as err:
LOG.error(_("Request to xCat server %(host)s failed: %(err)s") %
{'host': self.host, 'err': err})
raise exception.zVMxCatRequestFailed(xcatserver=self.host,
err=err)
res = self.conn.getresponse()
msg = res.read()
resp = {
'status': res.status,
'reason': res.reason,
'message': msg}
# NOTE(rui): Currently, only xCat returns 200 or 201 can be
# considered acceptable.
err = None
if method == "POST":
if res.status != 201:
err = str(resp)
else:
if res.status != 200:
err = str(resp)
if err is not None:
LOG.error(_("Request to xCat server %(host)s failed: %(err)s") %
{'host': self.host, 'err': err})
raise exception.zVMxCatRequestFailed(xcatserver=self.host,
err=err)
return resp
def xcat_request(method, url, body=None, headers={}):
conn = xCatConnection()
resp = conn.request(method, url, body, headers)
return load_xcat_resp(resp['message'])
def jsonloads(jsonstr):
try:
return jsonutils.loads(jsonstr)
except ValueError:
LOG.error(_("Respone is not in JSON format"))
raise exception.zVMJsonLoadsError()
def wrap_invalid_xcat_resp_data_error(function):
"""zVM driver get zVM hypervisor and virtual machine information
from xCat. xCat REST API response has its own fixed format(a JSON
stream). zVM driver abstract useful info base on the special format,
and raise exception if the data in incorrect format.
"""
@functools.wraps(function)
def decorated_function(*arg, **kwargs):
try:
return function(*arg, **kwargs)
except (ValueError, TypeError, IndexError) as err:
LOG.error(_('Invalid data returned from xCat: %s') % err)
raise exception.zVMInvalidxCatResponseDataError(msg=err)
except Exception as err:
raise
return decorated_function
@wrap_invalid_xcat_resp_data_error
def load_xcat_resp(message):
"""Abstract information from xCat REST response body.
As default, xCat response will in format of JSON and can be
converted to Python dictionary, would looks like:
{"data": [{"info": [info,]}, {"data": [data,]}, ..., {"error": [error,]}]}
Returns a Python dictionary, looks like:
{'info': [info,],
'data': [data,],
'error': [error,]}
"""
resp_list = jsonloads(message)['data']
keys = constants.XCAT_RESPONSE_KEYS
resp = {}
try:
for k in keys:
resp[k] = []
for d in resp_list:
for k in keys:
if d.get(k) is not None:
resp[k].append(d.get(k))
except Exception:
LOG.error(_("Invalid data returned from xCat: %s") % message)
raise exception.zVMInvalidxCatResponseDataError(msg=message)
if not verify_xcat_resp(resp):
LOG.error(_("Error returned from xCAT: %s") % message)
raise exception.zVMInvalidxCatResponseDataError(msg=message)
else:
return resp
@wrap_invalid_xcat_resp_data_error
def verify_xcat_resp(resp_dict):
"""Check whether xCAT REST API response contains an error."""
if resp_dict.get('error'):
if resp_dict['error'][0][0].find('Warning'):
return True
return False
else:
return True