def is_re_match(s, rs, flagstr=None):
    reflags = get_re_flags(flagstr)
    if re.search(rs, s, reflags):
        return True
    else:
        return False def filter_re_sub(s, rs, repl, flagstr=None, count=0):
    reflags = get_re_flags(flagstr)
    myre = re.compile(rs, reflags)
    return re.sub(myre, repl, s, count) def filter_re_escape(s):
    return re.escape(s) def filter_host(host):
    relevant_details = [d for d in host.monitoring_details if d.monitoring_type == "NAGIOSCONF"]
    if not relevant_details:
        snippet = "define host {\n  host_name                         %s" % host.host_name
        if len(host.contact_groups) > 0:
            snippet += "\n  contact_groups %s" % host.contact_groups
    else:
        snippet = "define host {\n  host_name                         %s\n" % host.host_name
        for detail in relevant_details:
            snippet += "  %-31s %s\n" % (detail.attribute, detail.value)
        snippet += "  use                             %s\n}\n" % (host.host_name + "_coshsh", )
        snippet += "define host {\n  name                            %s\n" % (host.host_name + "_coshsh", )
        snippet += "  register                        0"
        if len(host.contact_groups) > 0:
            snippet += "\n  contact_groups %s" % host.contact_groups
    macros = filter_custom_macros(host)
    if macros:
        snippet += macros
    return snippet def filter_service(application, service_description):
    relevant_details = [d for d in application.monitoring_details if d.monitoring_type == "NAGIOSCONF" and d.name == service_description]
    if not relevant_details:
        snippet = "define service {\n  service_description             %s" % service_description
        if len(application.contact_groups) > 0:
            snippet += "\n  contact_groups %s" % (application.contact_groups, )
    else:
        snippet = "define service {\n  service_description             %s\n" % service_description
        for detail in relevant_details:
            snippet += "  %-31s %s\n" % (detail.attribute, detail.value)
        snippet += "  use                             %s_%s\n}\n" % (service_description, application.host_name, )
        snippet += "define service {\n  name                            %s_%s\n" % (service_description, application.host_name)
        snippet += "  register                        0"
        if len(application.contact_groups) > 0:
            snippet += "\n  contact_groups %s" % application.contact_groups
    macros = filter_custom_macros(application)
    if macros:
        snippet += macros
    return snippet def filter_contact(contact):
    relevant_details = [d for d in contact.monitoring_details if d.monitoring_type == "NAGIOSCONF"]
    if not relevant_details:
        snippet = "define contact {\n  contact_name                      %s" % contact.contact_name
        if len(contact.contactgroups) > 0:
            snippet += "\n  contactgroups %s" % contact.contactgroups
    else:
        snippet = "define contact {\n  contact_name                      %s\n" % contact.contact_name
        for detail in relevant_details:
            snippet += "  %-31s %s\n" % (detail.attribute, detail.value)
        snippet += "  use                             %s\n}\n" % (contact.contact_name + "_coshsh", )
        snippet += "define contact {\n  name                            %s\n" % (contact.contact_name + "_coshsh", )
        snippet += "  register                        0"
        if len(contact.contactgroups) > 0:
            snippet += "\n  contactgroups %s" % contact.contactgroups
    macros = filter_custom_macros(contact)
    if macros:
        snippet += macros
    return snippet def filter_custom_macros(obj):
    snippet = ""
    macros = "\n".join("  %-31s %s" % (k, v) for k, v in
        sorted([x if x[0].startswith("_") else ("_" + x[0], x[1]) \
            for x in getattr(obj, "custom_macros", {}).items() + \
                 getattr(obj, "macros", {}).items()], key=lambda x: x[0]))
    if macros:
        snippet += "\n" + macros
    return snippet def filter_rfc3986(text):
    return 'rfc3986://' + urllib.pathname2url(text.encode('utf-8')) def global_environ(var, default=None):
    val = os.getenv(var, default)
    return val if val != None else "" class Item(object):
    template_cache = {}

    @classmethod
    def reload_template_path(cls):
        loader = FileSystemLoader(cls.templates_path)
        cls.env = Environment(loader=loader)
        cls.env.trim_blocks = True

    def __init__(self, params={}):
        #print "Item.__init__(", self.__class__.__name__
        self.log = logger

        for key in params:
            #print "set key", self.__class__.__name__, key
            if isinstance(params[key], basestring):
                setattr(self, key, params[key].strip())
            else:
                setattr(self, key, params[key])

        if not hasattr(self, "monitoring_details"):
            # if not pre-set through the class
            self.monitoring_details = []
        else:
            setattr(self, "monitoring_details", list(self.__class__.monitoring_details))
        self.config_files = {}

    def write_config(self, target_dir, want_tool=None):
        my_target_dir = os.path.join(target_dir, "hosts", self.host_name)
        if not os.path.exists(my_target_dir):
            os.makedirs(my_target_dir)
        for tool in self.config_files:
            if not want_tool or want_tool == tool:
                for file in self.config_files[tool]:
                    content = self.config_files[tool][file]
                    with open(os.path.join(my_target_dir, file), "w") as f:
                        f.write(content)

    def resolve_monitoring_details(self):
        details = [d for d in self.monitoring_details]
        for detail in details:
            property = detail.__class__.property
            if property == "generic":
                if detail.__class__.property_type == dict:
                    for key in detail.dictionary:
                        if key:
                            if ":" in key:
                                dictname, key = key.split(":")
                                try:
                                    setattr(getattr(self, dictname), key, detail.dictionary[dictname + ":" + key])
                                except Exception:
                                    setattr(self, dictname, EmptyObject())
                                    setattr(getattr(self, dictname), key, detail.dictionary[dictname + ":" + key])
                            else:
                                setattr(self, key, detail.dictionary[key])
                elif detail.__class__.property_type == list:
                    for key in detail.dictionary:
                        if key:
                            try:
                                getattr(self, key).extend(detail.dictionary[key])
                            except Exception:
                                setattr(self, key, detail.dictionary[key])
                else:
                    setattr(self, detail.attribute, detail.value)
            else:
                if detail.__class__.property_type == list:
                    if not hasattr(self, property):
                        setattr(self, property, [])
                    if hasattr(detail.__class__, "unique_attribute"):
                        # from the details remove an existing detail
                        # - which is of this class
                        # - which has the same unique_attr
                        if [o for o in getattr(self, property) if o.__class__ == detail.__class__ and getattr(o, detail.__class__.unique_attribute) == getattr(detail, detail.__class__.unique_attribute)]:
                            old = getattr(self, property)
                            new = [o for o in old if ((o.__class__ != detail.__class__) or (getattr(o, detail.__class__.unique_attribute) != getattr(detail, detail.__class__.unique_attribute)))]
                            new.append(detail)
                            setattr(self, property, new)
                        #print "res", self.host.host_name, detail.path
                        else:
                            getattr(self, property).append(detail)
                    else:
                        if hasattr(detail.__class__, "property_attr"):
                            getattr(self, property).append(getattr(detail, getattr(detail.__class__, "property_attr")))
                        else:
                            getattr(self, property).append(detail)
                elif detail.__class__.property_type == dict:
                    if not hasattr(self, property):
                        setattr(self, property, {})
                    if hasattr(detail, "key") and hasattr(detail, "value"):
                        getattr(self, property)[detail.key] = detail.value
                else:
                    if getattr(detail.__class__, 'property_flat', False):
                        # ex. MonitoringDetailRole: appl.role == str instead of appl.role.role == str
                        setattr(self, property, getattr(detail, property))
                    else:
                        setattr(self, property, detail)
            # This detail has been resolved. Maybe we run resolve_monitoring_details
            # later again, we don't want to repeat ourselves.
            self.monitoring_details.remove(detail)
        self.wemustrepeat()
        # example: if we have self.ports
        # and self.ports[0] has an inside property ports
        # and self has d default property port (set in __init__)
        # replace the self.port by self.ports[0].port
        for one_property in [detail.__class__.property.rstrip('s') for detail in details if detail.__class__.property_type == list and not hasattr(detail.__class__, "unique_attribute") and not hasattr(detail.__class__, "property_attr") and detail.__class__.property.endswith('s') and hasattr(self, detail.__class__.property.rstrip('s'))]:
            if hasattr(getattr(self, one_property + 's')[0], one_property):
                setattr(self, one_property, getattr(getattr(self, one_property + 's')[0], one_property))

    def wemustrepeat(self):
        """
        This method is called by some classes if different monitoring_details need to interact.
        Ex. (username/password was set in a LOGIN detail and/or a URL detail
        """
        pass

    def pythonize(self):
        if hasattr(self, "templates"):
            self.templates = self.templates.split(',')
        if hasattr(self, "contactgroups"):
            self.contactgroups = self.contactgroups.split(',')
        if hasattr(self, "contact_groups"): #! contacts have contact!_!groups
            self.contact_groups = self.contact_groups.split(',')
        if hasattr(self, "contacts"):
            self.contacts = self.contacts.split(',')
        if hasattr(self, "hostgroups"):
            self.hostgroups = self.hostgroups.split(',')
        if hasattr(self, "servicegroups"):
            self.servicegroups = self.servicegroups.split(',')
        if hasattr(self, "members"):
            self.members = self.members.split(',')
        if hasattr(self, "parents"):
            self.parents = self.parents.split(',')
        if hasattr(self, "host_notification_commands"):
            self.host_notification_commands = self.host_notification_commands.split(',')
        if hasattr(self, "service_notification_commands"):
            self.service_notification_commands = self.service_notification_commands.split(',')

    def depythonize(self):
        if hasattr(self, "templates"):
            self.templates = ",".join(self.templates)
        if hasattr(self, "contactgroups"):
            self.contactgroups = ",".join(sorted(self.contactgroups, cmp=locale.strcoll))
        if hasattr(self, "contact_groups"):
            self.contact_groups = ",".join(sorted(self.contact_groups, cmp=locale.strcoll))
        if hasattr(self, "contacts"):
            self.contacts = ",".join(sorted(self.contacts, cmp=locale.strcoll))
        if hasattr(self, "hostgroups"):
            self.hostgroups = ",".join(sorted(self.hostgroups, cmp=locale.strcoll))
        if hasattr(self, "servicegroups"):
            self.servicegroups = ",".join(sorted(self.servicegroups, cmp=locale.strcoll))
        if hasattr(self, "members"):
            self.members = ",".join(sorted(self.members, cmp=locale.strcoll))
        if hasattr(self, "parents"):
            self.parents = ",".join(sorted(self.parents, cmp=locale.strcoll))
        if hasattr(self, "host_notification_commands"):
            self.host_notification_commands = ",".join(sorted(self.host_notification_commands, cmp=locale.strcoll))
        if hasattr(self, "service_notification_commands"):
            self.service_notification_commands = ",".join(sorted(self.service_notification_commands, cmp=locale.strcoll))

    def render_cfg_template(self, jinja2, template_cache, name, output_name, suffix, for_tool, **kwargs):
        try:
            if not name in template_cache:
                template_cache[name] = jinja2.env.get_template(name + ".tpl")
                logger.info("load template " + name)
        except TemplateSyntaxError as e:
            logger.critical("%s template %s has an error in line %d: %s" % (self.__class__.__name__, name, e.lineno, e.message))
        except TemplateNotFound:
            logger.error("cannot find template " + name)
        except Exception as exp:
            logger.critical("error in template %s (%s,%s)" (name, exp.__class__.__name__, exp))

        if name in template_cache:
            # transform hostgroups, contacts, etc. from list to string
            self.depythonize()
            try:
                if not for_tool in self.config_files:
                    self.config_files[for_tool] = {}
                if suffix:
                    self.config_files[for_tool][output_name + "." + suffix] = template_cache[name].render(kwargs).encode('utf-8')
                else:
                    # files without suffix
                    self.config_files[for_tool][output_name] = template_cache[name].render(kwargs)
            except Exception as exp:
                if hasattr(self, "fingerprint"):
                    logger.critical("render exception in template %s for %s %s: %s" % (name, self, self.fingerprint(), exp))
                else:
                    logger.critical("render exception in template %s for %s: %s" % (name, self, exp))
            # transform hostgroups, contacts, etc. back to lists
            self.pythonize()

    def render(self, template_cache, jinja2, recipe):
        if not hasattr(self, 'template_rules'):
            return
        for rule in self.template_rules:
            render_this = False
            try:
                if not rule.needsattr:
                    # TemplateRule(template="os_solaris_default")
                    render_this = True
    
                elif (hasattr(self, rule.needsattr) and rule.isattr == None):
                    # TemplateRule(needsattr=None,template="os_solaris_default")
                    render_this = True
    
                elif hasattr(self, rule.needsattr) and not isinstance (getattr(self, rule.needsattr), list) and ((getattr(self, rule.needsattr) == rule.isattr) or re.match(rule.isattr, getattr(self, rule.needsattr))):
                    # TemplateRule(needsattr="cluster", isattr="veritas",
                    #     template="os_solaris_cluster_veritas")
                    render_this = True
    
                elif hasattr(self, rule.needsattr) and isinstance (getattr(self, rule.needsattr), list) and [elem for elem in getattr(self, rule.needsattr) if (elem == rule.isattr or re.match(rule.isattr, elem))]:
                    # TemplateRule(needsattr="hostgroups",
                    #     isattr="cluster_solaris_veritas.*",
                    #     template="os_solaris_cluster_veritas")
                    render_this = True
                    
                elif hasattr(self, rule.needsattr) and isinstance (getattr(self, rule.needsattr), list):
                    pass
            except Exception, e:
                logger.critical("error in %s template rules. please check %s. Error was: %s" % (self.__class__.__name__, rule, str(e)))

            if render_this:
                if rule.unique_config and isinstance(rule.unique_attr, basestring) and hasattr(self, rule.unique_attr):
                    self.render_cfg_template(jinja2, template_cache, rule.template, rule.unique_config % getattr(self, rule.unique_attr), rule.suffix, rule.for_tool, **dict([(rule.self_name, self), ("recipe", recipe)]))
                elif rule.unique_config and isinstance(rule.unique_attr, list) and reduce(lambda x, y: x and y, [hasattr(self, ua) for ua in rule.unique_attr]):
                    self.render_cfg_template(jinja2, template_cache, rule.template, rule.unique_config % tuple([getattr(self, a) for a in rule.unique_attr]), rule.suffix, rule.for_tool, **dict([(rule.self_name, self), ("recipe", recipe)]))
                else:
                    self.render_cfg_template(jinja2, template_cache, rule.template, rule.template, rule.suffix, rule.for_tool, **dict([(rule.self_name, self), ("recipe", recipe)]))

    def fingerprint(self):
        try:
            return "%s+%s+%s" % (self.host_name, self.name, self.type)
        except: pass
        try:
            return "%s" % (self.host_name, )
        except: pass
        raise "impossible fingerprint" class Application(coshsh.item.Item):

    class_factory = []
    lower_columns = ['name', 'type', 'component', 'version', 'patchlevel']

    def __init__(self, params):
        #print "Application init", self.__class__, self.__class__.__name__, len(self.__class__.class_factory)
        if self.__class__.__name__ == "Application":
            for c in self.__class__.lower_columns:
                try:
                    params[c] = params[c].lower()
                except Exception:
                    if c in params:
                        params[c] = None
            newcls = self.__class__.get_class(params)
            if newcls:
                self.__class__ = newcls
                self.contact_groups = []
                super(Application, self).__init__(params)
                self.__init__(params)
                self.fingerprint = lambda s=self:s.__class__.fingerprint(params)
            else:
                logger.debug('this will be Generic %s' % params)
                self.__class__ = GenericApplication
                self.contact_groups = []
                super(Application, self).__init__(params)
                self.__init__(params)
                #raise ApplicationNotImplemented
                self.fingerprint = lambda s=self:s.__class__.fingerprint(params)
        else:
            pass

    @classmethod
    def fingerprint(self, params={}):
        return "%s+%s+%s" % (params["host_name"], params["name"], params["type"])

    def _i_init__(self, params={}):
        super(Application, self).__init__(params)
        self.contact_groups = []

    def create_servicegroups(self):
        pass

    def create_contacts(self):
        pass

    def create_templates(self):
        pass

    @classmethod
    def init_classes(cls, classpath):
        sys.dont_write_bytecode = True
        for p in [p for p in reversed(classpath) if os.path.exists(p) and os.path.isdir(p)]:
            for module, path in [(item, p) for item in sorted(os.listdir(p), reverse=True) if item[-3:] == ".py" and (item.startswith('app_') or item.startswith('os_'))]:
                try:
                    path = os.path.abspath(path)
                    fp, filename, data = imp.find_module(module.replace('.py', ''), [path])
                    toplevel = imp.load_source(module.replace(".py", ""), filename)
                    for cl in inspect.getmembers(toplevel, inspect.isfunction):
                        if cl[0] ==  "__mi_ident__":
                            cls.class_factory.append([path, module, cl[1]])
                except Exception, e:
                    print e
                finally:
                    if fp:
                        fp.close()
        #print ".............fill %s / %s woth %s" % (cls, cls.__name__, cls.class_factory)


    @classmethod
    def get_class(cls, params={}):
        #print "getclass from cache", cls, cls.__name__,  cls.class_factory
        for path, module, class_func in reversed(cls.class_factory):
            try:
                #print "get_class trys", path, module, class_func
                newcls = class_func(params)
                #print "get_class says", newcls.__module__, sys.modules[newcls.__module__].__file__
                if newcls:
                    return newcls
            except Exception:
                pass
        logger.debug("found no matching class for this monitoring item %s" % params)
        return None class Contact(coshsh.item.Item):

    class_factory = []
    lower_columns = []

    template_rules = [
        coshsh.templaterule.TemplateRule(
            template="contact",
            self_name="contact",
            unique_attr="contact_name", unique_config="contact_%s",
        )
    ]

    def __init__(self, params):
        #print "Contact init", self.__class__, self.__class__.__name__, len(self.__class__.class_factory)
        if self.__class__.__name__ == "Contact":
            for c in self.__class__.lower_columns:
                try:
                    params[c] = params[c].lower()
                except Exception:
                    if c in params:
                        params[c] = None
            self.email = None
            self.pager = None
            self.address1 = None
            self.address2 = None
            self.address3 = None
            self.address4 = None
            self.address5 = None
            self.address6 = None
            self.can_submit_commands = False
            self.contactgroups = []
            self.custom_macros = {}
            newcls = self.__class__.get_class(params)
            if newcls:
                self.__class__ = newcls
                super(Contact, self).__init__(params)
                self.__init__(params)
                self.fingerprint = lambda s=self:s.__class__.fingerprint(params)
            else:
                logger.debug('this will be Generic %s' % params)
                self.__class__ = GenericContact
                self.contactgroups = []
                super(Contact, self).__init__(params)
                self.__init__(params)
                self.fingerprint = lambda s=self:s.__class__.fingerprint(params)
            if not hasattr(self, 'host_notification_period') or not self.host_notification_period:
                self.host_notification_period = self.notification_period
                logger.debug('no column host_notification_period found use notification_period instead')
            if not hasattr(self, 'service_notification_period') or not self.service_notification_period:
                self.service_notification_period = self.notification_period
                logger.debug('no column service_notification_period found use notification_period instead')
        else:
            pass

    def clean_name(self):
        self.name = clean_umlauts(self.name)


    @classmethod
    def fingerprint(self, params):
        return "+".join([unicode(params.get(a, "")) for a in ["name", "type", "address", "userid"]])

    def __str__(self):
        fipri = " ".join([unicode(getattr(self, a, "")) for a in ["name", "type", "address", "userid"]])
        grps = ",".join(self.contactgroups)
        return unicode("contact %s groups (%s)" % (fipri, grps))

    @classmethod
    def init_classes(cls, classpath):
        sys.dont_write_bytecode = True
        for p in [p for p in reversed(classpath) if os.path.exists(p) and os.path.isdir(p)]:
            for module, path in [(item, p) for item in sorted(os.listdir(p), reverse=True) if item[-3:] == ".py" and (item.startswith('contact_') or item == 'contact.py')]:
                try:
                    path = os.path.abspath(path)
                    fp, filename, data = imp.find_module(module.replace('.py', ''), [path])
                    toplevel = imp.load_source(module.replace(".py", ""), filename)
                    for cl in inspect.getmembers(toplevel, inspect.isfunction):
                        if cl[0] ==  "__mi_ident__":
                            cls.class_factory.append([path, module, cl[1]])
                except Exception, e:
                    print e
                finally:
                    if fp:
                        fp.close()
        #print ".............fill %s / %s woth %s" % (cls, cls.__name__, cls.class_factory)


    @classmethod
    def get_class(cls, params={}):
        #print "getclass from cache", cls, cls.__name__,  cls.class_factory
        for path, module, class_func in reversed(cls.class_factory):
            try:
                #print "get_class trys", path, module, class_func
                newcls = class_func(params)
                #print "get_class says", newcls.__module__, sys.modules[newcls.__module__].__file__
                if newcls:
                    return newcls
            except Exception:
                pass
        logger.debug("found no matching class for this monitoring item %s" % params)
        return None class HostGroup(coshsh.item.Item):

    template_rules = [
        coshsh.templaterule.TemplateRule(needsattr=None, 
            template="hostgroup", 
            self_name="hostgroup",
            unique_attr="hostgroup_name", unique_config="hostgroup_%s",
        ),
    ]


    def __init__(self, params={}):
        self.members = []
        self.contacts = []
        self.contactgroups = []
        self.templates = []
        superclass = super(HostGroup, self)
        superclass.__init__(params)
        

    def is_correct(self):
        return True


    def write_config(self, target_dir, want_tool=None):
        my_target_dir = os.path.join(target_dir, "hostgroups", self.hostgroup_name)
        if not os.path.exists(my_target_dir):
            os.makedirs(my_target_dir)
        for tool in self.config_files:
            if not want_tool or want_tool == tool:
                for file in self.config_files[tool]:
                    content = self.config_files[tool][file]
                    with open(os.path.join(my_target_dir, file), "w") as f:
                        f.write(content)



    def create_members(self):
        pass


    def create_contacts(self):
        pass


    def create_templates(self):
        pass class MonitoringDetail(coshsh.item.Item):

    class_factory = []
    lower_columns = ['name', 'type', 'application_name', 'application_type']

    def __init__(self, params):
        #print "Detail init", self.__class__, self.__class__.__name__, len(self.__class__.class_factory)
        if self.__class__.__name__ == "MonitoringDetail":
            for c in self.__class__.lower_columns:
                try:
                    params[c] = params[c].lower()
                except Exception:
                    if c in params:
                        params[c] = None
            # name, type is preferred, because a detail can also be a host detail
            # application_name, application_type is ok too. in any case these will be internally used
            if 'name' in params:
                params['application_name'] = params['name']
                del params['name']
            if 'type' in params:
                params['application_type'] = params['type']
                del params['type']
            newcls = self.__class__.get_class(params)
            if newcls:
                self.__class__ = newcls
                super(MonitoringDetail, self).__init__(params)
                self.__init__(params)
            else:
                logger.info("monitoring detail of type %s for host %s / appl %s had a problem" % (params["monitoring_type"], params.get("host_name", "unkn. host"), params.get("application_name", "unkn. application")))
                raise MonitoringDetailNotImplemented
        else:
            pass

    def fingerprint(self):
        # it does not make sense to construct an id from the random attributes
        # id is used in self.add('details')
        return id(self)

    def application_fingerprint(self):
        if hasattr(self, 'application_name') and self.application_name and hasattr(self, 'application_type') and self.application_type:
            return "%s+%s+%s" % (self.host_name, self.application_name, self.application_type)
        elif self.host_name:
            return "%s" % (self.host_name, )
        raise "impossible fingerprint"

    @classmethod
    def init_classes(cls, classpath):
        sys.dont_write_bytecode = True
        for p in [p for p in reversed(classpath) if os.path.exists(p) and os.path.isdir(p)]:
            for module, path in [(item, p) for item in os.listdir(p) if item[-3:] == ".py" and item.startswith('detail_')]:
                try:
                    path = os.path.abspath(path)
                    fp, filename, data = imp.find_module(module.replace('.py', ''), [path])
                    toplevel = imp.load_source(module.replace(".py", ""), filename)
                    for cl in inspect.getmembers(toplevel, inspect.isfunction):
                        if cl[0] ==  "__detail_ident__":
                            cls.class_factory.append([path, module, cl[1]])
                except Exception, e:
                    print e
                finally:
                    if fp:
                        fp.close()


    @classmethod
    def get_class(cls, params={}):
        #print "getclass from cache", cls, cls.__name__, len(cls.class_factory)
        for path, module, class_func in reversed(cls.class_factory):
            try:
                #print "get_class trys", path, module, class_func
                newcls = class_func(params)
                #print "get_class says", newcls
                if newcls:
                    return newcls
            except Exception:
                pass
        logger.debug("found no matching class for this monitoring detail %s" % params)
        return None

    def __eq__(self, other):
        return ((self.monitoring_type, str(self.monitoring_0)) == (other.monitoring_type, str(other.monitoring_0)))

    def __ne__(self, other):
        return ((self.monitoring_type, str(self.monitoring_0)) != (other.monitoring_type, str(other.monitoring_0)))

    def __lt__(self, other):
        return ((self.monitoring_type, str(self.monitoring_0)) < (other.monitoring_type, str(other.monitoring_0)))

    def __le__(self, other):
        return ((self.monitoring_type, str(self.monitoring_0)) <= (other.monitoring_type, str(other.monitoring_0)))

    def __gt__(self, other):
        return ((self.monitoring_type, str(self.monitoring_0)) > (other.monitoring_type, str(other.monitoring_0)))

    def __ge__(self, other):
        return ((self.monitoring_type, str(self.monitoring_0)) >= (other.monitoring_type, str(other.monitoring_0)))

    def __repr__(self):
        return "%s %s" % (self.monitoring_type, str(self.monitoring_0)) class Datasource(object):

    my_type = 'datasource'
    class_factory = []

    def __init__(self, **params):
        #print "datasourceinit with", self.__class__
        for key in [k for k in params if k.startswith("recipe_")]:
            setattr(self, key, params[key])
            short = key.replace("recipe_", "")
            if not short in params:
                params[short] = params[key]
        for key in params.keys():
            if isinstance(params[key], basestring):
                params[key] = re.sub('%.*?%', substenv, params[key])
        if self.__class__ == Datasource:
            #print "generic ds", params
            newcls = self.__class__.get_class(params)
            if newcls:
                #print "i rebless anon datasource to", newcls, params
                self.__class__ = newcls
                self.__init__(**params)
            else:
                logger.critical('datasource for %s is not implemented' % params)
                #print "i raise DatasourceNotImplemented"
                raise DatasourceNotImplemented
        else:
            setattr(self, 'name', params["name"])
            self.objects = {}
            pass
        # i am a generic datasource
        # i find a suitable class
        # i rebless
        # i call __init__

    def open(self, **kwargs):
        pass

    def read(self, **kwargs):
        pass

    def close(self):
        pass

    def add(self, objtype, obj):
        try:
            self.objects[objtype][obj.fingerprint()] = obj
        except Exception:
            self.objects[objtype] = {}
            self.objects[objtype][obj.fingerprint()] = obj
        if objtype == 'applications':
            if self.find('hosts', obj.host_name):
                setattr(obj, 'host', self.get('hosts', obj.host_name))

    def get(self, objtype, fingerprint):
        try:
            return self.objects[objtype][fingerprint]
        except Exception:
            # should be None
            return None
            return 'i do not exist. no. no!'

    def getall(self, objtype):
        try:
            return self.objects[objtype].values()
        except Exception:
            return []

    def find(self, objtype, fingerprint):
        return objtype in self.objects and fingerprint in self.objects[objtype]

    @classmethod
    def init_classes(cls, classpath):
        sys.dont_write_bytecode = True
        for p in [p for p in reversed(classpath) if os.path.exists(p) and os.path.isdir(p)]:
            for module, path in [(item, p) for item in os.listdir(p) if item[-3:] == ".py" and item.startswith('datasource_')]:
                try:
                    #print "try ds", module, path
                    path = os.path.abspath(path)
                    fp, filename, data = imp.find_module(module.replace('.py', ''), [path])
                    toplevel = imp.load_source(module.replace(".py", ""), filename)
                    for cl in inspect.getmembers(toplevel, inspect.isfunction):
                        if cl[0] ==  "__ds_ident__":
                            cls.class_factory.append([path, module, cl[1]])
                except Exception, exp:
                    logger.critical("could not load datasource %s from %s: %s" % (module, path, exp))
                finally:
                    if fp:
                        fp.close()


    @classmethod
    def get_class(cls, params={}):
        #print "get_classhoho", cls, len(cls.class_factory), cls.class_factory
        for path, module, class_func in cls.class_factory:
            try:
                #print "try", path, module, class_func
                newcls = class_func(params)
                if newcls:
                    return newcls
            except Exception ,exp:
                dsname = 'INVALID' if 'name' not in params else params['name']
                print 'Datasource.get_class exception while trying module "%s" for datasource "%s": %s %s' % \
                      (os.path.join(path, module), dsname, type(exp), exp)
                pass
        logger.debug("found no matching class for this datasource %s" % params) class DatasourceCorrupt(Exception):
    pass class DatasourceNotReady(Exception):
    # datasource is currently being updated
    pass class DatasourceNotAvailable(Exception):
    pass class DatasourceNotCurrent(Exception):
    # datasources was not updated lately.
    # it makes no sense to continue.
    pass class Datarecipient(object):

    my_type = 'datarecipient'
    class_factory = []

    def __init__(self, **params):
        #print "datarecipientinit with", self.__class__
        for key in [k for k in params if k.startswith("recipe_")]:
            setattr(self, key, params[key])
            short = key.replace("recipe_", "")
            if not short in params:
                params[short] = params[key]
        for key in params.keys():
            if isinstance(params[key], basestring):
                params[key] = re.sub('%.*?%', substenv, params[key])
        if self.__class__ == Datarecipient:
            #print "generic ds", params
            newcls = self.__class__.get_class(params)
            if newcls:
                #print "i rebless anon datarecipient to", newcls, params
                self.__class__ = newcls
                self.__init__(**params)
            else:
                logger.critical('datarecipient for %s is not implemented' % params)
                #print "i raise DatarecipientNotImplemented"
                raise DatarecipientNotImplemented
        else:
            setattr(self, 'name', params["name"])
            self.objects = {}
            pass

    def load(self, filter=None, objects={}):
        logger.info('load items to %s' % (self.name, ))
        self.objects = objects

    def get(self, objtype, fingerprint):
        try:
            return self.objects[objtype][fingerprint]
        except Exception:
            # should be None
            return 'i do not exist. no. no!'

    def getall(self, objtype):
        try:
            return self.objects[objtype].values()
        except Exception:
            return []

    def find(self, objtype, fingerprint):
        return objtype in self.objects and fingerprint in self.objects[objtype]

    def item_write_config(self, obj, dynamic_dir, objtype, want_tool=None):
        my_target_dir = os.path.join(dynamic_dir, objtype)
        if not os.path.exists(my_target_dir):
            os.makedirs(my_target_dir)
        for tool in obj.config_files:
            if not want_tool or want_tool == tool:
                for file in obj.config_files[tool]:
                    content = obj.config_files[tool][file]
                    with open(os.path.join(my_target_dir, file), "w") as f:
                        f.write(content)

    def output(self, filter=None, want_tool=None):
        for hostgroup in self.objects['hostgroups'].values():
            self.item_write_config(hostgroup, self.dynamic_dir, "hostgroups", want_tool)
        for host in self.objects['hosts'].values():
            self.item_write_config(host, self.dynamic_dir, os.path.join("hosts", host.host_name), want_tool)
        for app in self.objects['applications'].values():
            self.item_write_config(app, self.dynamic_dir, os.path.join("hosts", app.host_name), want_tool)
        for cg in self.objects['contactgroups'].values():
            self.item_write_config(cg, self.dynamic_dir, "contactgroups", want_tool)
        for c in self.objects['contacts'].values():
            self.item_write_config(c, self.dynamic_dir, "contacts", want_tool)

    def count_objects(self):
        try:
            hosts = len([name for name in os.listdir(os.path.join(self.dynamic_dir, 'hosts')) if os.path.isdir(os.path.join(self.dynamic_dir, 'hosts', name))])
            apps = len([app for host in os.listdir(os.path.join(self.dynamic_dir, 'hosts')) if os.path.isdir(os.path.join(self.dynamic_dir, 'hosts', host)) for app in os.listdir(os.path.join(self.dynamic_dir, 'hosts', host)) if app != 'host.cfg' and os.path.getsize(os.path.join(self.dynamic_dir, 'hosts', host, app)) != 0])
            return (hosts, apps)
        except Exception:
            return (0, 0)

    def count_before_objects(self):
        self.old_objects = self.count_objects()

    def count_after_objects(self):
        self.new_objects = self.count_objects()

    def prepare_target_dir(self):
        pass

    def cleanup_target_dir(self):
        pass

    def too_much_delta(self):
        # self.old_objects = (hosts_before, apps_before)
        # self.new_objects = (hosts_after, apps_after)
        # self.max_delta = (%hosts, %apps)
        # if %hosts or %apps is negative, then accept an increase of any size
        # only shrinking numbers are a problem
        try:
            self.delta_hosts = 100 * (self.new_objects[0] - self.old_objects[0]) / self.old_objects[0]
        except Exception, e:
            # before we had 0 hosts. accept this initial increase
            self.delta_hosts = 0
        try:
            self.delta_services = 100 * (self.new_objects[1] - self.old_objects[1]) / self.old_objects[1]
        except Exception, e:
            # before we had 0 applications
            self.delta_services = 0
        if not self.max_delta:
            return False
        #
        #  before  after  delta
        #  0       10     0
        #  10      0      -100
        #  10      8      -20
        if self.max_delta[0] < 0 and self.delta_hosts < self.max_delta[0]:
            print "neg and shruink"
            return True
        if self.max_delta[1] < 0 and self.delta_services < self.max_delta[1]:
            return True
        if self.max_delta[0] >= 0 and abs(self.delta_hosts) > self.max_delta[0]:
            return True
        if self.max_delta[1] >= 0 and abs(self.delta_services) > self.max_delta[1]:
            return True
        return False


    @classmethod
    def init_classes(cls, classpath):
        sys.dont_write_bytecode = True
        for p in [p for p in reversed(classpath) if os.path.exists(p) and os.path.isdir(p)]:
            for module, path in [(item, p) for item in sorted(os.listdir(p), reverse=True) if item[-3:] == ".py" and item.startswith('datarecipient_')]:
                try:
                    #print "try dr", module, path
                    path = os.path.abspath(path)
                    fp, filename, data = imp.find_module(module.replace('.py', ''), [path])
                    toplevel = imp.load_source(module.replace(".py", ""), filename)
                    for cl in inspect.getmembers(toplevel, inspect.isfunction):
                        if cl[0] ==  "__dr_ident__":
                            cls.class_factory.append([path, module, cl[1]])
                except Exception, exp:
                    logger.critical("could not load datarecipient %s from %s: %s" % (module, path, exp))
                finally:
                    if fp:
                        fp.close()


    @classmethod
    def get_class(cls, params={}):
        #print "get_classhoho", cls, len(cls.class_factory), cls.class_factory
        for path, module, class_func in reversed(cls.class_factory):
            try:
                #print "try", path, module, class_func
                newcls = class_func(params)
                if newcls:
                    return newcls
            except Exception ,exp:
                print "Datarecipient.get_class exception", exp
                pass
        logger.debug("found no matching class for this datarecipient %s" % params) class DatarecipientCorrupt(Exception):
    pass class DatarecipientNotReady(Exception):
    # datarecipient is currently being updated
    pass class DatarecipientNotAvailable(Exception):
    pass class DatarecipientNotCurrent(Exception):
    # datarecipients was not updated lately.
    # it makes no sense to continue.
    pass def compare_attr(key, params, strings):
    if not isinstance(strings, list):
        strings = [strings]
    if key in params:
        if params[key] == None:
            return False
        elif [str1 for str1 in strings if re.match(str1, params[key], re.IGNORECASE)]:
            return True
    return False def substenv(matchobj):
    if matchobj.group(0).replace('%', '') in os.environ.keys():
        return os.environ[matchobj.group(0).replace('%', '')]
    else:
        return matchobj.group(0) def switch_logging(**kwargs):
    logdir = kwargs.get("logdir", setup_logging.logdir)
    logfile = kwargs.get("logfile", setup_logging.logfile)
    backup_count = kwargs.get("backup_count", setup_logging.backup_count)
    logdir = setup_logging.logdir if logdir == None else logdir
    logfile = setup_logging.logfile if logfile == None else logfile
    backup_count = setup_logging.backup_count if backup_count == None else backup_count
    abs_logfile = logfile if os.path.isabs(logfile) else os.path.join(logdir, logfile)
    if abs_logfile == setup_logging.abs_logfile:
        return
    if not os.path.exists(os.path.dirname(abs_logfile)):
        os.mkdir(os.path.dirname(abs_logfile))
    logger = logging.getLogger(setup_logging.logger_name)
    logger.debug("Logger switches to " + abs_logfile)
    # remove the txt_handler
    logger.removeHandler(setup_logging.txt_handler)
    for handler in logger.handlers:
        if hasattr(handler, "baseFilename"):
            logger.removeHandler(handler)
    txt_handler = RotatingFileHandler(abs_logfile, backupCount=backup_count, maxBytes=20*1024*1024, delay=True)
    txt_handler.setFormatter(setup_logging.log_formatter)
    txt_handler.setLevel(setup_logging.txtloglevel)
    logger.addHandler(txt_handler)
import sys
import os
import re
import inspect
import time
import logging
import errno
import coshsh
from jinja2 import FileSystemLoader, Environment, TemplateSyntaxError, TemplateNotFound
from coshsh.jinja2_extensions import is_re_match, filter_re_sub, filter_re_escape, filter_host, filter_service, filter_contact, filter_custom_macros, filter_rfc3986, global_environ
from coshsh.item import Item
from coshsh.application import Application
from coshsh.contact import Contact
from coshsh.hostgroup import HostGroup
from coshsh.monitoringdetail import MonitoringDetail
from coshsh.datasource import Datasource, DatasourceCorrupt, DatasourceNotReady, DatasourceNotAvailable, DatasourceNotCurrent
from coshsh.datarecipient import Datarecipient, DatarecipientCorrupt, DatarecipientNotReady, DatarecipientNotAvailable, DatarecipientNotCurrent
from coshsh.util import compare_attr, substenv, switch_logging
            elif fingerprint.startswith('*'):
                generic_details.append(detail)
            else:
                logger.info("found a %s detail %s for an unknown application %s" % (detail.__class__.__name__, detail, fingerprint))
        for detail in generic_details:
            dfingerprint = detail.application_fingerprint()
            if dfingerprint == '*':
                for host in self.objects['hosts'].values():
                    host.monitoring_details.insert(0, detail)
            else:
                for app in self.objects['applications'].values():
                    afingerprint = app.fingerprint()
                    if dfingerprint[1:] == afingerprint[afingerprint.index('+'):]:
                        app.monitoring_details.insert(0, detail)


        for host in self.objects['hosts'].values():
            host.resolve_monitoring_details()
            for key in [k for k in host.__dict__.keys() if not k.startswith("__") and isinstance(getattr(host, k), (list, tuple)) and k not in ["templates"]]:
                getattr(host, key).sort()
            host.create_templates()
            host.create_hostgroups()
            host.create_contacts()

        orphaned_applications = []
        for app in self.objects['applications'].values():
            try:
                setattr(app, 'host', self.objects['hosts'][app.host_name])
                app.resolve_monitoring_details()
                for key in [k for k in app.__dict__.keys() if not k.startswith("__") and isinstance(getattr(app, k), (list, tuple))]:
                    # sort monitoring_type/monitoring_0 to bring some order into services,filesystems etc.
                    getattr(app, key).sort()
                app.create_templates()
                app.create_servicegroups()
                app.create_contacts()
            except KeyError:
                logger.info("application %s %s refers to non-existing host %s" % (app.name, app.type, app.host_name))
                orphaned_applications.append(app.fingerprint())
        for oa in orphaned_applications:
            del self.objects['applications'][oa]

        # load the hostgroups-objects after application procession because
        # this allows modification of self.host.hostgroups
        # in application.wemustrepeat()
        for host in self.objects['hosts'].values():
            for hostgroup in host.hostgroups:
                try:
                    self.objects['hostgroups'][hostgroup].append(host.host_name)
                except Exception:
                    self.objects['hostgroups'][hostgroup] = []
                    self.objects['hostgroups'][hostgroup].append(host.host_name)
        for (hostgroup_name, members) in self.objects['hostgroups'].items():
            logger.debug("creating hostgroup %s" % hostgroup_name)
            self.objects['hostgroups'][hostgroup_name] = HostGroup({ "hostgroup_name" : hostgroup_name, "members" : members})
            self.objects['hostgroups'][hostgroup_name].create_templates()
            self.objects['hostgroups'][hostgroup_name].create_contacts()

        return True
 
    def render(self):
        template_cache = {}
        for host in self.objects['hosts'].values():
            host.render(template_cache, self.jinja2, self)
        for app in self.objects['applications'].values():
            # because of this __new__ construct the Item.searchpath is
            # not inherited. Needs to be done explicitely
            app.render(template_cache, self.jinja2, self)
        for cg in self.objects['contactgroups'].values():
            cg.render(template_cache, self.jinja2, self)
        for c in self.objects['contacts'].values():
            c.render(template_cache, self.jinja2, self)
        for hg in self.objects['hostgroups'].values():
            hg.render(template_cache, self.jinja2, self)
        # you can put anything in objects (Item class with own templaterules)
        for item in sum([self.objects[itype].values() for itype in self.objects if itype not in ['hosts', 'applications', 'details', 'contactgroups', 'contacts', 'hostgroups']], []):
            # first check hasattr, because somebody may accidentially
            # add objects which are not a subclass of Item.
            # (And such a stupid mistake crashes coshsh-cook)
            if hasattr(item, 'config_files') and not item.config_files:
                # has not been populated with content in the datasource
                # (like bmw appmon timeperiods)
                item.render(template_cache, self.jinja2, self)
            
    def count_before_objects(self):
        for datarecipient in self.datarecipients:
            datarecipient.count_before_objects()
        self.old_objects = (sum([dr.old_objects[0] for dr in self.datarecipients], 0), sum([dr.old_objects[1] for dr in self.datarecipients], 0))

    def count_after_objects(self):
        for datarecipient in self.datarecipients:
            datarecipient.count_after_objects()
        self.new_objects = (sum(0, [dr.new_objects[0] for dr in self.datarecipients]), sum(0, [dr.new_objects[1] for dr in self.datarecipients]))

    def cleanup_target_dir(self):
        for datarecipient in self.datarecipients:
            datarecipient.cleanup_target_dir()

    def prepare_target_dir(self):
        for datarecipient in self.datarecipients:
            datarecipient.prepare_target_dir()

    def output(self):
        cleaned_dirs = []
        for datarecipient in self.datarecipients:
            datarecipient.count_before_objects()
            datarecipient.load(None, self.objects)
            if hasattr(datarecipient, 'dynamic_dir') and datarecipient.dynamic_dir not in cleaned_dirs:
                # do not clean a target dir where another datarecipient already wrote it's files
                datarecipient.cleanup_target_dir()
                cleaned_dirs.append(datarecipient.dynamic_dir)
            datarecipient.prepare_target_dir()
            datarecipient.output()

    def read(self):
        return self.objects


    def init_class_cache(self):
        Datasource.init_classes(self.classes_path)
        logger.debug("init Datasource classes (%d)" % len(Datasource.class_factory))

        Datarecipient.init_classes(self.classes_path)