summaryrefslogtreecommitdiffstats
path: root/lmi/scripts/common/versioncheck/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'lmi/scripts/common/versioncheck/__init__.py')
-rw-r--r--lmi/scripts/common/versioncheck/__init__.py146
1 files changed, 146 insertions, 0 deletions
diff --git a/lmi/scripts/common/versioncheck/__init__.py b/lmi/scripts/common/versioncheck/__init__.py
new file mode 100644
index 0000000..c7e595f
--- /dev/null
+++ b/lmi/scripts/common/versioncheck/__init__.py
@@ -0,0 +1,146 @@
+# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and documentation are
+# those of the authors and should not be interpreted as representing official
+# policies, either expressed or implied, of the FreeBSD Project.
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+"""
+Package with utilities for checking availability of profiles or CIM classes.
+Version requirements can also be specified.
+"""
+
+import functools
+from pyparsing import ParseException
+
+from lmi.scripts.common import Configuration
+from lmi.scripts.common import errors
+from lmi.scripts.common.versioncheck import parser
+
+def cmp_profiles(fst, snd):
+ """
+ Compare two profiles by their version.
+
+ :returns:
+ * -1 if the *fst* profile has lower version than *snd*
+ * 0 if their versions are equal
+ * 1 otherwise
+ :rtype: int
+ """
+ fstver = fst.RegisteredVersion
+ sndver = snd.RegisteredVersion
+ if fstver == sndver:
+ return 0
+ return -1 if parser.cmp_version(fstver, sndver) else 1
+
+def get_profile_version(conn, name, cache=None):
+ """
+ Get version of registered profile on particular broker. Queries
+ ``CIM_RegisteredProfile`` and ``CIM_RegisteredSubProfile``. The latter
+ comes in question only when ``CIM_RegisteredProfile`` does not yield any
+ matching result.
+
+ :param conn: Connection object.
+ :param string name: Name of the profile which must match value of *RegisteredName*
+ property.
+ :param dictionary cache: Optional cache where the result will be stored for
+ later use. This greatly speeds up evaluation of several expressions refering
+ to same profiles or classes.
+ :returns: Version of matching profile found. If there were more of them,
+ the highest version will be returned. ``None`` will be returned when no matching
+ profile or subprofile is found.
+ :rtype: string
+ """
+ if cache and name in cache:
+ return cache[(conn.uri, name)]
+ insts = conn.root.interop.wql('SELECT * FROM CIM_RegisteredProfile'
+ ' WHERE RegisteredName=\"%s\"' % name)
+ regular = set(i for i in insts if i.classname.endswith('RegisteredProfile'))
+ if regular: # select instances of PG_RegisteredProfile if available
+ insts = regular
+ else: # otherwise fallback to PG_RegisteredSubProfile instances
+ insts = set(i for i in insts if i not in regular)
+ if not insts:
+ ret = None
+ else:
+ ret = sorted(insts, cmp=cmp_profiles)[-1].RegisteredVersion
+ if cache is not None:
+ cache[(conn.uri, name)] = ret
+ return ret
+
+def get_class_version(conn, name, namespace=None, cache=None):
+ """
+ Query broker for version of particular CIM class. Version is stored in
+ ``Version`` qualifier of particular CIM class.
+
+ :param conn: Connection object.
+ :param string name: Name of class to query.
+ :param string namespace: Optional CIM namespace. Defaults to configured namespace.
+ :param dictionary cache: Optional cache used to speed up expression prrocessing.
+ :returns: Version of CIM matching class. Empty string if class is registered but
+ is missing ``Version`` qualifier and ``None`` if it is not registered.
+ :rtype: string
+ """
+ if namespace is None:
+ namespace = Configuration.get_instance().namespace
+ if cache and (namespace, name) in cache:
+ return cache[(conn.uri, namespace, name)]
+ ns = conn.get_namespace(namespace)
+ cls = getattr(ns, name, None)
+ if not cls:
+ ret = None
+ else:
+ quals = cls.wrapped_object.qualifiers
+ if 'Version' not in quals:
+ ret = ''
+ else:
+ ret = quals['Version'].value
+ if cache is not None:
+ cache[(conn.uri, namespace, name)] = ret
+ return ret
+
+def eval_respl(expr, conn, namespace=None, cache=None):
+ """
+ Evaluate LMIReSpL expression on particular broker.
+
+ :param string expr: Expression to evaluate.
+ :param conn: Connection object.
+ :param string namespace: Optional CIM namespace where CIM classes will be
+ searched.
+ :param dictionary cache: Optional cache speeding up evaluation.
+ :returns: ``True`` if requirements in expression are satisfied.
+ :rtype: boolean
+ """
+ if namespace is None:
+ namespace = Configuration.get_instance().namespace
+ stack = []
+ pvget = functools.partial(get_profile_version, conn, cache=cache)
+ cvget = functools.partial(get_class_version, conn,
+ namespace=namespace, cache=cache)
+ pr = parser.bnf_parser(stack, pvget, cvget)
+ pr.parseString(expr, parseAll=True)
+ # Now evaluate starting non-terminal created on stack.
+ return stack[0]()
+