diff options
-rwxr-xr-x | openlmi-cimmof | 342 |
1 files changed, 0 insertions, 342 deletions
diff --git a/openlmi-cimmof b/openlmi-cimmof deleted file mode 100755 index 4f4ccaa..0000000 --- a/openlmi-cimmof +++ /dev/null @@ -1,342 +0,0 @@ -#!/usr/bin/env python -# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved. -# -# This library 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 2.1 of the License, or (at your option) any later version. -# -# This library 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 this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -""" -Allows to modify Pegasus repository with declarations in mof files. -Pegasus must be running for this script to work. It depends on cimmof -binary, which is online compilator of MOF files for pegasus. - -It works in this way: - 1. cimmof is called on input mof files - 2. its output (xml) is then parsed by pywbem functions producing - CIM objects (instances of ``pywbem.CIMClass`` and - ``pywbem.CIMInstance``) - 3. these objects are then used in calls to - ``{Create,Modify}{Instance,Class}`` - -*Note* that only Class and Instance declarations are supported. - - This is due to limitations in pywbem parser. - - Although this could be avoided by calling wbemexec on generated XML. -""" - -import argparse -import re -import logging -import subprocess -import sys -import pywbem -import xml.dom.minidom as dom - -DEFAULT_NAMESPACE = "root/cimv2" -DEFAULT_CIMMOF = "cimmof" - -RE_COMMENT = re.compile(r'\s*<!--.*?-->\s*', re.DOTALL) - -logging.basicConfig(level=logging.ERROR, - format="%(levelname)s - %(message)s") -LOG = logging.getLogger(__name__) - -def die(msg, *args, **kwargs): - """ - Exit with error printed to stderr. - """ - LOG.error(msg, *args, **kwargs) - sys.exit(1) - -def xml_cleanup(xml_str): - """ - Return xml string without comments and whitespaces. - """ - # remove comments - without_comments = "".join(RE_COMMENT.split(xml_str)) - # remove whitespaces - return "".join(l.strip() for l in without_comments.split("\n")) - -def get_objects_from_mofs(cimmof, namespace, *mofs): - """ - Call cimmof binary with mofs as input and obtain class/instance - declarations in XML. - - Return list of pywbem CIM abstractions for each declaration. - """ - cmd = [cimmof, '--xml', '-n', namespace] - objects = [] - for mof in mofs: - process = subprocess.Popen(cmd, stdin=subprocess.PIPE, - stdout=subprocess.PIPE, stderr=sys.stderr) - (out, _) = process.communicate(mof.read()) - if len(out.strip()) == 0: - die("%s returned empty string, is Pegasus running?" % cimmof) - parsed_dom = dom.parseString(xml_cleanup(out)) - # we cannot use pywbem.parse_cim because it does not support - # DECLARATION element, but we can parse individual values - for decl in parsed_dom.getElementsByTagName('VALUE.OBJECT'): - # pywbem first makes tupletree from dom: - # (name, attributes, children) - # and from it generates pywbem CIM abstractions - (_name, _attrs, obj) = pywbem.tupleparse.parse_value_object( - pywbem.dom_to_tupletree(decl)) - objects.append(obj) - return objects - -def get_instance_path(conn, namespace, instance, classes): - """ - Obtains a class declaration from cimom for given instance and builds - a path from it. - - :param conn is a wbem connection - :param namespace (``str``) is a target namespace of instance - :param instance (``CIMInstance``) contains class name and is used to get - values of key properties. Is modified by adding path to it. - :param classes (``dict``) is a cache of classes obtained from cimom. - Its items are in form: ``(classname, CIMClass)``. - - Return ``CIMInstanceName``. - """ - if not instance.classname in classes: - classes[instance.classname] = conn.GetClass(instance.classname, - IncludeQualifiers=True, - namespace=namespace) - cls = classes[instance.classname] - keys = [p.name for p in cls.properties.values() if "Key" in p.qualifiers] - path = pywbem.CIMInstanceName(instance.classname, namespace=namespace) - for key in keys: - if not key in instance: - die("instance of %s is missing key property \"%s\"", - instance.classname, key) - path[key] = instance[key] - instance.path = path - return path - -def create_class(conn, cls, namespace, classes, allow_update=False): - """ - Create class or modify it if already present. - - :param classes: (``dict``) a cache of classes obtained from cimom. - :param allow_update: (``bool``) whether to modify existing class. - """ - try: - if not cls.classname in classes: - classes[cls.classname] = conn.GetClass(cls.classname, - IncludeQualifiers=True, - namespace=namespace) - except pywbem.CIMError as err: - if err.args[0] != pywbem.CIM_ERR_NOT_FOUND: - raise - if cls.classname in classes: - if not allow_update: - LOG.error("class %s already exists", cls.classname) - else: - conn.ModifyClass(cls, namespace=namespace) - LOG.info("modified class %s", cls.classname) - else: - conn.CreateClass(cls, namespace=namespace) - LOG.info("created class %s", cls.classname) - -def create_instance(conn, inst, namespace, classes, allow_update=False): - """ - Create instance or modify it if already present. - - :param classes: (``dict``) a cache of classes obtained from cimom. - :param allow_update: (``bool``) whether to modify existing instance. - """ - path = get_instance_path(conn, namespace, inst, classes) - present = None - try: - present = conn.GetInstance(path) - except pywbem.CIMError as err: - if err.args[0] != pywbem.CIM_ERR_NOT_FOUND: - raise - if present is not None: - try: - if allow_update: - conn.ModifyInstance(inst) - LOG.info("modified instance for path %s", path) - else: - LOG.error("instance %s already exists", path) - except pywbem.CIMError as err: - if err.args[0] == pywbem.CIM_ERR_NOT_SUPPORTED: - LOG.error("ModifyInstance() is not supported for class %s," - " please remove the instance first", - inst.classname) - else: - raise - - else: - conn.CreateInstance(inst) - LOG.info("created instance for path %s", path) - -def reorder_objects(cmd, objects): - """ - Reorder classes and instances so that dependent objects are handled - later. - - Classes can depend between each other in two ways: - 1. one inherits from another - 2. one refers to another (associations) - - The first case can be solved by counting number of parents, that are - also to be removed. Class with highest number will be created as last. - - The second one applies only to associations, which can not refer - to each other. Let's just append them after non-associations. - - :param cmd: (``str``) can be "create" or "delete". In latter case the - result is reversed, so the dependent classes/instances are removed - as first. - - *Note* this does not handle dependencies between instances. - """ - cls_list = [] - assoc_list = [] - cls_dict = set( c.classname.lower() - for c in objects if isinstance(c, pywbem.CIMClass)) - # (class name, number of superclasses in cls_dict) - cls_deps = pywbem.NocaseDict() - inst_list = [] - for obj in objects: - if isinstance(obj, pywbem.CIMClass): - if 'association' in obj.qualifiers: - assoc_list.append(obj) - else: - cls_list.append(obj) - cls_deps[obj.classname] = 0 - parent = obj.superclass - while parent in cls_dict: - cls_deps[obj.classname] += 1 - parent = cls_dict[parent].classname - else: # no specific reordering of instances - inst_list.append(obj) - key_func = lambda c: (cls_deps[c.classname], c.classname) - cls_list = sorted(cls_list, key=key_func) - assoc_list = sorted(assoc_list, key=key_func) - - result = cls_list + assoc_list + inst_list - if cmd == "delete": - result.reverse() - return result - -def push_to_repo(namespace, cmd, objects, allow_update=False): - """ - Create or delete desired objects in Pegasus repository. - - :param cmd: (``string``) is one of { 'create' | 'delete' } - :param objects: (``list``) is a list of pywbem CIM abstractions - created from mofs. They will be operated upon. - """ - if not isinstance(namespace, basestring): - raise TypeError("namespace must be string") - if not cmd in ('create', 'delete'): - raise ValueError('cmd must be either "create" or "delete"') - classes = pywbem.NocaseDict() - conn = pywbem.PegasusUDSConnection() - objects = reorder_objects(cmd, objects) - for obj in objects: - try: - if cmd == "create": - if isinstance(obj, pywbem.CIMClass): - create_class(conn, obj, namespace, classes, allow_update) - elif isinstance(obj, pywbem.CIMInstance): - create_instance(conn, obj, namespace, classes, - allow_update) - else: - LOG.error("unsupported object for creation: %s", - obj.__class__.__name__) - - else: - try: - if isinstance(obj, pywbem.CIMClass): - conn.DeleteClass(obj.classname, namespace=namespace) - LOG.info("deleted class %s", obj.classname) - elif isinstance(obj, pywbem.CIMInstance): - path = get_instance_path(conn, namespace, obj, classes) - conn.DeleteInstance(path) - LOG.info("deleted instance %s", path) - else: - LOG.error("unsupported object for deletion: %s", - obj.__class__.__name__) - except pywbem.CIMError as err: - if err.args[0] == pywbem.CIM_ERR_NOT_FOUND: - LOG.warn("%s not present in repository", - obj if isinstance(obj, pywbem.CIMClass) - else path) - else: - raise - - except pywbem.CIMError as err: - if err.args[0] in (pywbem.CIM_ERR_INVALID_PARAMETER, ): - LOG.warn("failed to %s %s: %s", cmd, obj, err) - else: - raise - -def parse_cmd_line(): - """ - Parse command line and return options. - """ - parser = argparse.ArgumentParser( - usage="%(prog)s [options] {create,delete} mof [mof ...]", - description="Allows to create/delete instances and classes" - " declared in MOF files. It operates only on Pegasus broker" - " that needs to be up and running.") - parser.add_argument('--cimmof', default=DEFAULT_CIMMOF, - help="Path to cimmof binary to use.") - #parser.add_argument('--xml', action='store_true', default=False, - #help="Do not execute any action on cimom, just print the" - #" xml to stdout.") - parser.add_argument('-n', '--namespace', default=DEFAULT_NAMESPACE, - help="Target CIM Repository namespace.") - parser.add_argument('-v', '--verbose', action='store_true', - default=False, help="Be more verbosive on output.") - - mof_parser = argparse.ArgumentParser(add_help=False) - mof_parser.add_argument('mof', nargs='+', - type=argparse.FileType('r'), - default=sys.stdin, - help="Mof files containing declarations of classes and instances" - " to be installed or removed from Pegasus broker.") - - command = parser.add_subparsers(title="Operation commands", - dest="command", - help="Operation on declarations.") - create_cmd = command.add_parser('create', parents=[mof_parser], - help='Create instances and classes listed in mof files.') - create_cmd.add_argument('-u', '--allow-update', - action="store_true", default=False, - help="Allow update of class declaration if it already exists.") - command.add_parser('delete', parents=[mof_parser], - help="Delete instances and classes listed in mof files.") - - args = parser.parse_args() - return args - -def main(): - """ - The main functionality of script. - """ - args = parse_cmd_line() - if args.verbose: - LOG.setLevel(logging.INFO) - # parse mofs and build list of pywbem objects - objs = get_objects_from_mofs(args.cimmof, args.namespace, *args.mof) - if not objs: - die("no declarations found!") - push_to_repo(args.namespace, args.command, objs, - getattr(args, 'allow_update', False)) - -if __name__ == '__main__': - main() - |