Source code for taurus.core.tango.tangovalidator

#!/usr/bin/env python

# ###########################################################################
#
# This file is part of Taurus
#
# http://taurus-scada.org
#
# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
#
# Taurus is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Taurus 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Taurus.  If not, see <http://www.gnu.org/licenses/>.
#
# ###########################################################################

"""This module contains the base taurus name validator classes
"""

__all__ = [
    "TangoAuthorityNameValidator",
    "TangoDeviceNameValidator",
    "TangoAttributeNameValidator",
]

__docformat__ = "restructuredtext"

from taurus.core.taurusvalidator import (
    TaurusAttributeNameValidator,
    TaurusDeviceNameValidator,
    TaurusAuthorityNameValidator,
)
from taurus.core.util.fqdn import fqdn_no_alias


# todo: I do not understand the behaviour of getNames for Auth, Dev and Attr in
#      the case when the fullname does not match the regexp. For Auth it
#      returns a 3-tuple, for devs a 2-tuple and for attrs and conf a single
#      None. This is not coherent to what the method returns when it matches
#      the regexp (always a 3-tuple)


[docs] class TangoAuthorityNameValidator(TaurusAuthorityNameValidator): """Validator for Tango authority names. Apart from the standard named groups (scheme, authority, path, query and fragment), the following named groups are created: - host: tango host name, without port. - port: port number """ scheme = "(tango|tango-nodb)" authority = ( r"//((?P<host>([\w\-_]+\.)*[\w\-_]+):(?P<port>\d{1,5})" + r"|([\w\-_]+\.)*[\w\-_]+:\d{1,5}" + r"(,([\w\-_]+\.)*[\w\-_]+:\d{1,5})+)" ) path = "(?!)" query = "(?!)" fragment = "(?!)" default_scheme = "tango"
[docs] def getUriGroups(self, name, strict=None): """Reimplementation of getUriGroups to fix the host and authority name using fully qualified domain name for the host. """ ret = TaurusAuthorityNameValidator.getUriGroups(self, name, strict) if ret is not None and ret.get("host", None) is not None: ret["host"] = fqdn_no_alias(ret["host"]) ret["authority"] = "//{host}:{port}".format(**ret) return ret
[docs] class TangoDeviceNameValidator(TaurusDeviceNameValidator): """Validator for Tango device names. Apart from the standard named groups (scheme, authority, path, query and fragment), the following named groups are created: - devname: device name (either alias or slashed name) - [_devalias]: device alias - [_devslashname]: device name in slashed (a/b/c) form - [host] as in :class:`TangoAuthorityNameValidator` - [port] as in :class:`TangoAuthorityNameValidator` Note: brackets on the group name indicate that this group will only contain a string if the URI contains it. """ scheme = "(tango|tango-nodb)" authority = TangoAuthorityNameValidator.authority path = ( r"/?(?P<devname>((?P<_devalias>[^/?#:]+)|" + r"(?P<_devslashname>[^/?#:]+/[^/?#:]+/[^/?#:]+)))" ) query = "(?!)" fragment = "(?P<_fragmentdb>dbase=(yes|no)$)" default_scheme = "tango" names_cache = {}
[docs] def getUriGroups(self, name, strict=None): """Reimplementation of getUriGroups to fix the host and authority name using fully qualified domain name for the host. """ ret = TaurusDeviceNameValidator.getUriGroups(self, name, strict) if ret is not None and ret.get("host", None) is not None: ret["host"] = fqdn_no_alias(ret["host"]) ret["authority"] = "//{host}:{port}".format(**ret) # use special scheme "tango-nodb" if the name contains #dbase=no if ret is not None and ret.get("_fragmentdb", None) == "dbase=no": ret["scheme"] = "tango-nodb" return ret
[docs] def getNames(self, fullname, factory=None, queryAuth=True): """reimplemented from :class:`TaurusDeviceNameValidator`. It accepts an extra keyword arg `queryAuth` which, if set to False, will prevent the validator from trying to query a TaurusAuthority to obtain missing info such as the devslashname <--> devalias correspondence. """ if fullname in self.names_cache: return self.names_cache[fullname] groups = self.getUriGroups(fullname) if groups is None: return None default_authority = None if factory is None: from taurus import Factory factory = Factory(scheme="tango") default_authority = factory.get_default_tango_host() if default_authority is None: import tango tango_host = tango.ApiUtil.get_env_var("TANGO_HOST") v = TangoAuthorityNameValidator() g = v.getUriGroups("tango://{}".format(tango_host)) default_authority = g.get("authority") authority = groups.get("authority") if authority is None: groups["authority"] = authority = default_authority db = None if queryAuth and groups["scheme"] != "tango-nodb": try: db = factory.getAuthority("tango:{}".format(authority)) except Exception: pass # note, since we validated, we either have alias or slashname (not # both) _devalias = groups.get("_devalias") _devslashname = groups.get("_devslashname") if _devslashname is None and db is not None: # get _devslashname from the alias using the DB _devslashname = db.getElementFullName(_devalias) groups["_devslashname"] = _devslashname if _devslashname is None: # if we still do not have a slashname, we can only give the short return None, None, _devalias # we can now construct everything. First the complete: complete = "{scheme}:{authority}/{_devslashname}".format(**groups) # then the normal if authority.lower() == default_authority.lower(): normal = "{_devslashname}".format(**groups) else: normal = "{authority}/{_devslashname}".format(**groups) # and finally the short if _devalias is not None: short = _devalias else: if db is not None: # get the alias from the DB (if it is defined) short = db.getElementAlias(_devslashname) or _devslashname else: short = _devslashname self.names_cache[fullname] = complete, normal, short return complete, normal, short
@property def nonStrictNamePattern(self): """In non-strict mode, allow double-slash even if there is no Authority. (e.g., "tango://a/b/c" passes this non-strict form) """ pattern = ( r"^((?P<scheme>%(scheme)s)://)?" + r"((?P<authority>%(authority)s)(?=/))?" + r"(?P<path>%(path)s)" + r"(\?(?P<query>%(query)s))?" + r"(#%(fragment)s)?$" ) authority = r"(?P<host>([\w\-_]+\.)*[\w\-_]+):(?P<port>\d{1,5})" path = ( "/?(?P<devname>((?P<_devalias>([^/?#:]+))|" + "(?P<_devslashname>[^/?#:]+/[^/?#:]+/[^/?#:]+)))" ) return pattern % dict( scheme=self.scheme, authority=authority, path=path, query="(?!)", fragment="(?!)", )
[docs] class TangoAttributeNameValidator(TaurusAttributeNameValidator): """Validator for Tango attribute names. Apart from the standard named groups (scheme, authority, path, query and fragment), the following named groups are created: - attrname: attribute name including device name - _shortattrname: attribute name excluding device name - devname: as in :class:`TangoDeviceNameValidator` - [_devalias]: as in :class:`TangoDeviceNameValidator` - [_devslashname]: as in :class:`TangoDeviceNameValidator` - [host] as in :class:`TangoAuthorityNameValidator` - [port] as in :class:`TangoAuthorityNameValidator` - [cfgkey] same as fragment (for bck-compat use only) Note: brackets on the group name indicate that this group will only contain a string if the URI contains it. """ scheme = "(tango|tango-nodb)" authority = TangoAuthorityNameValidator.authority path = "(?P<attrname>{0}/(?P<_shortattrname>[^/?:#]+))".format( TangoDeviceNameValidator.path ) query = "(?!)" fragment = "((?P<_fragmentdb>dbase=(yes|no)$)|(?P<cfgkey>[^# ]*))" default_scheme = "tango"
[docs] def getUriGroups(self, name, strict=None): """Reimplementation of getUriGroups to fix the host and authority name using fully qualified domain name for the host. """ ret = TaurusAttributeNameValidator.getUriGroups(self, name, strict) if ret is not None: if ret.get("host", None) is not None: ret["host"] = fqdn_no_alias(ret["host"]) auth = "//{host}:{port}".format(**ret) ret["authority"] = auth # use special scheme "tango-nodb" if the name contains #dbase=no if ret is not None and ret.get("_fragmentdb", None) == "dbase=no": ret["scheme"] = "tango-nodb" return ret
[docs] def getNames(self, fullname, factory=None, queryAuth=True, fragment=False): """Returns the complete and short names""" groups = self.getUriGroups(fullname) if groups is None: return None complete, normal, short = None, None, groups.get("_shortattrname") # reuse the getNames from the Device validator... devname = fullname.rsplit("#", 1)[0].rsplit("/", 1)[0] v = TangoDeviceNameValidator() devcomplete, devnormal, _ = v.getNames( devname, factory=factory, queryAuth=queryAuth ) if devcomplete is not None: complete = "{}/{}".format(devcomplete, short) if devnormal is not None: normal = "{}/{}".format(devnormal, short) # return fragment if requested (ignores the special #dbase=yes|no case) if fragment: key = groups.get("cfgkey", None) return complete, normal, short, key return complete, normal, short
@property def nonStrictNamePattern(self): """In non-strict mode, allow double-slash even if there is no Authority. Also allow old-style "?configuration[=cfgkey]" instead of fragment. If cfgkey is present, it is also stored in the "fragment" named group. For example, "tango://a/b/c/d?configuration=label" passes this non-strict form, and the named group "fragment" will contain "label" """ pattern = ( r"^((?P<scheme>%(scheme)s)://)?" + r"((?P<authority>%(authority)s)(?=/))?" + r"(?P<path>%(path)s)" + r"(\?(?P<query>%(query)s))?" + r"(#%(fragment)s)?$" ) authority = r"(?P<host>([\w\-_]+\.)*[\w\-_]+):(?P<port>\d{1,5})" query = "configuration(=(?P<fragment>(?P<cfgkey>[^# ]+)))?" return pattern % dict( scheme=self.scheme, authority=authority, path=self.path, query=query, fragment="(?!)", )