polardbxengine/storage/ndb/mcc/util.py

251 lines
7.9 KiB
Python

# Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
Common utilities needed by the configurator backend modules.
"""
import socket
import xml.sax
import errno
import stat
import logging
import contextlib
_logger = logging.getLogger(__name__)
@contextlib.contextmanager
def socket_shutter(sock):
"""x"""
try:
yield sock
finally:
sock.shutdown(socket.SHUT_RDWR)
sock.close()
def is_set(val, mask):
"""Return True if bits in mask are also set in val."""
return val & mask == mask
def is_dir(attrs):
"""Returns true of attrs is the stat attrs for a directory, False otherwise."""
return stat.S_ISDIR(attrs.st_mode)
def mock_msg(cmd, body):
"""Wrap body in a dummy message header for cmd."""
return {'head': {'seq': 0, 'cmd' : cmd}, 'body': body, 'reply': None}
def try_connect(url_host, port, success=True, retdict={}):
"""Test connectability of url_host and port. Return success if connection attempt
was successful. If the connection attempt throws a socket.error and its errno is
a key in retdict, the corresponding retdict value is returned. Any other exception
propagated.
"""
s = None
try:
s = socket.create_connection((url_host, port))
except socket.error as err:
if retdict.has_key(err.errno):
return retdict[err.errno]
_logger.debug('err.errno='+str(err.errno)+' not found in retdict='+str(retdict))
raise
else:
return success
finally:
if s:
s.shutdown(socket.SHUT_RDWR)
s.close()
def _retdict(name, val):
rd = {getattr(errno, name) : val}
wsa = 'WSA'+name
if hasattr(errno, wsa):
rd[getattr(errno, wsa)] = val
return rd
def is_port_available(url_host, port):
"""Predicate for determining if a port is available. Returns False if connection to
url_host,port succeeds, and True if it throws connection refused."""
return try_connect(url_host, port, False, _retdict('ECONNREFUSED', True))
class NoPortAvailableException(Exception):
"""Exception thrown if no unused port can be found."""
def __init__(self, url_host, bp, ep):
self.host = url_host
self.bp = bp
self.ep = ep
def __str__(self):
return 'No available ports in range [{_.bp},{_.ep}] on url_host {_.url_host}'.format(_=self)
def first_available_port(url_host, bport, eport):
"""Locate the first unused port on url_host in the interval [bport,eport]. Returns
the port number if successful. Throws NoPortAvailableException otherwise."""
for p in range(bport, eport+1):
if is_port_available(url_host, p):
return p
raise NoPortAvailableException(url_host, bport, eport)
def html_rep(obj):
"""Format an object in an html-friendly way. That is; convert any < and > characters
found in the str() representation to the corresponding html-escape sequence."""
s = str(obj)
if s == '':
s = repr(obj)
return s.strip().replace('<', '&lt;').replace('>', '&gt;')
def get_val(d, k, default=None):
"""
@type d: dict
@param d: Value store
@type k: str
@param k: Key to get value for
@param default: Return this value if key L{k} cannot be found
@rtype: str
@return: Value corresponding to key L{k} or L{default}
There was no sanity check, added.
"""
if not d is None:
if not d.has_key(k):
return default
return d[k]
else:
return default
def get_fmask(perm, role):
"""Get the stat permission mask value corresponding to perm (X,R,W)
and role (USR, GRP, OTH)."""
return getattr(stat, 'S_I'+perm+role)
class Param(dict):
"""Specialization of dict representing a command line parameter.
Extending dict allows easy serialization as Json."""
def __init__(self, name, val=None, sep='='):
if type(name) == dict or type(name) == Param:
for k in name.keys():
self[k] = name[k]
return
self['name'] = name
if val:
self['val'] = val
self['sep'] = sep
assert self['name'] == name
def __str__(self):
if self.has_key('val'):
return self['name']+get_val(self, 'sep', '=') +str(self['val'])
return self['name']
def params_to_cmdv(executable, params):
"""Join executable with params passed in."""
return[executable] + [str(Param(p)) for p in params['param']]
class ConvertToPython(xml.sax.handler.ContentHandler):
"""Specialized handler to convert xml into a graph of builtin, json-serializable Python
objects (dicts and lists)."""
def __init__(self):
self._estack = []
def startElement(self, name, attrs):
"""Creates a new attribute dict and pushes it on the element stack."""
#print('start( ', name, ')')
a = {}
for an in attrs.getNames():
a[an] = attrs.getValue(an)
try:
a[an] = float(a[an])
a[an] = int(a[an])
except:
pass
self._estack.append([name, a])
def endElement(self, name):
"""Pops the element stack and inserts the popped element as a
child of the new top"""
#print('end(', name, ')')
if len(self._estack) > 1:
e = self._estack.pop()
self._estack[-1].append(e)
def get_obj(self):
"""Returns the resulting object. Convenience method for accessing the only
remaining element on the element stack."""
assert len(self._estack) == 1
return self._estack[0]
def xml_to_python(xml_str):
"""Convenience function for parsing an xml-string with sax.parseString
and ConvertToPython."""
assert xml_str != None
cp = ConvertToPython()
xml.sax.parseString(xml_str, cp, xml.sax.handler.ErrorHandler())
return cp.get_obj()
def _parse_until_delim(ctx, fld, delim):
""" Return False unless delim exists in ctx['str']. Assign ctx['str'] excluding
delim to ctx[fld] and assign ctx[str] to the remainder, excluding delim, and
return True otherwise."""
i = ctx['str'].find(delim)
if i == -1:
return False
ctx[fld] = ctx['str'][0:i]
ctx['str'] = ctx['str'][i+len(delim):]
return True
def parse_empty_line(ctx):
"""Return False unless ctx[str] starts with an empty line. Consume the empty line
and return True otherwise."""
if ctx['str'].startswith('\n'):
ctx['str'] = ctx['str'][1:]
return True
return False
def parse_property(ctx, delim):
"""Return False unless key and value parsing succeeds. Add kv-pair to ctx['properties']
and return True otherwise."""
if _parse_until_delim(ctx, 'key', delim) and _parse_until_delim(ctx, 'val', '\n'):
ctx['properties'][ctx['key']] = ctx['val']
return True
return False
def parse_properties(ctx, delim='='):
"""Return False unless ctx['str'] is a list of properties, a single property or an empty line.
Parse the list of properties and return True otherwise."""
return parse_property(ctx, delim) and parse_properties(ctx, delim) or parse_empty_line(ctx)
class bcolors:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
CRITICAL = '\033[101m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'