Ejemplo n.º 1
0
    def add_timeseries(self, path, *args, **kwargs):
        """Add a timeseries to the smap server at the given path.  This will
        generate a UUID for the timeseries.

        direct form 
        :param path a Timeseries instance
        
        simple form 
        :param args[0] is a uuid instance, or a key to generate a uuid with by combining it with the root uuid.
        :param args[1] and kwargs are arguments passed to the Timeseries constructor.  Therefore you have to include at least the UnitofMeasure

        :param boolean replace: (kwarg) replace an existing timeseries at that path instead of throwing an exception
        :param boolean recurse: recursively create parent collections instead of thrwoing an exception.  Default is True.

        :raises: :py:class:`SmapException` if the parent isn't a collection or the path already exists.
        """ 
        replace = kwargs.pop('replace', False)
        recurse = kwargs.pop('recurse', True)
        klass = kwargs.pop('klass', Timeseries)

        if len(args) == 0 or \
                not ITimeseries.providedBy(args[0]) and not IActuator.providedBy(args[0]):
            if len(args) == 2:
                if not isinstance(args[0], uuid.UUID):
                    id = self.uuid(args[0], namespace=kwargs.get('namespace', None))
                else:
                    id = args[0]
                args = args[1:]
            elif len(args) == 1:
                id = self.uuid(util.norm_path(path), kwargs.get('namespace', None))
            else:
                id = self.uuid(util.norm_path(path))
#                 raise SmapException("SmapInstance.add_timeseries may only be called "
#                                     "with two or three arguments")

            kwargs.pop('namespace', None)
            timeseries = klass(id, *args, **kwargs)
            if id != args[0]:
                setattr(timeseries, "key", args[0])
        else:
            timeseries = args[0]

        path = util.split_path(path)
        if recurse: self._add_parents(path)
        parent = self.get_collection(util.join_path(path[:-1]))

        if not replace and util.join_path(path) in self.OBJS_PATH:
            raise SmapException("add_timeseries: path " + str(path) + " exists!")
        if not parent:
            raise SmapException("add_timeseries: parent is not a collection!")
        parent.add_child(path[-1])

        # place the new timeseries into the uuid and path tables
        self.OBJS_UUID[timeseries['uuid']] = timeseries
        self.OBJS_PATH[util.join_path(path)] = timeseries
        timeseries.inst = self
        setattr(timeseries, 'path', util.join_path(path))
        if not self.loading: self.reports.update_subscriptions()
        return timeseries
Ejemplo n.º 2
0
def dump(inst, file):
    """Dump an existing :py:class:`~smap.core.SmapInstance` object to a conf file

:param smap.Core.SmapInstance inst: the object to dump
:param string file: config filename
:raises IOError: if writing to the file fails
"""
    conf = ConfigParser.ConfigParser('', ordereddict.OrderedDict)
    conf.optionxform = str

    q = ['/']
    while len(q) > 0:
        cur = inst.get_collection(q[0])
        if cur and cur.has_key('Contents') and not q[0] in inst.drivers:
            for child in cur['Contents']:
                q.append(util.norm_path(q[0] + '/' + child))
        _save_path(conf, inst, q[0])

        if conf.get(q[0], 'type') == 'Timeseries':
            for k, v in core.Timeseries.DEFAULTS.iteritems():
                if conf.has_option(q[0], k) and \
                        conf.get(q[0], k) == str(v):
                    conf.remove_option(q[0], k)

        q.pop(0)

    with open(file, 'w') as fp:
        conf.write(fp)
Ejemplo n.º 3
0
def dump(inst, file):
    """Dump an existing :py:class:`~smap.core.SmapInstance` object to a conf file

:param smap.Core.SmapInstance inst: the object to dump
:param string file: config filename
:raises IOError: if writing to the file fails
"""
    conf = ConfigParser.ConfigParser('', ordereddict.OrderedDict)
    conf.optionxform = str

    q = ['/']
    while len(q) > 0:
        cur = inst.get_collection(q[0])
        if cur and cur.has_key('Contents') and not q[0] in inst.drivers:
            for child in cur['Contents']:
                q.append(util.norm_path(q[0] + '/' + child))
        _save_path(conf, inst, q[0])

        if conf.get(q[0], 'type') == 'Timeseries':
            for k, v in core.Timeseries.DEFAULTS.iteritems():
                if conf.has_option(q[0], k) and \
                        conf.get(q[0], k) == str(v):
                    conf.remove_option(q[0], k)

        q.pop(0)

    with open(file, 'w') as fp:
        conf.write(fp)
Ejemplo n.º 4
0
    def publish(self, path, val, prepend=False):
        """Publish a new reading to the stream identified by a path.

        Not thread safe.
        """
        path = util.norm_path(path)
        for sub in self.subscribers:
            if path in sub['Topics']:
                sub['PendingData'].add(path, val)
Ejemplo n.º 5
0
    def publish(self, path, val, prepend=False):
        """Publish a new reading to the stream identified by a path.

        Not thread safe.
        """
        path = util.norm_path(path)
        for sub in self.subscribers:
            if path in sub['Topics']:
                sub['PendingData'].add(path, val)
Ejemplo n.º 6
0
    def _lookup_r(self, id, pred=None):
        """Lookup recursively in the resource hierarchy, starting with the
        resource identifed by "id".  Returns a list of elements for which
        "pred" returns True"""
        rv = {}
        q = [id]
        root_path = getattr(self.lookup(id), 'path')

        while len(q) > 0:
            cur = self.lookup(q.pop(0))
            if ICollection.providedBy(cur):
                for child in cur['Contents']:
                    q.append(getattr(cur, 'path') + '/' + child)
            if cur and (not pred or pred(cur)):
                rvpath = util.norm_path(getattr(cur, 'path')[len(root_path):])
                rv[rvpath] = cur
        return rv
Ejemplo n.º 7
0
    def __init__(self, path, inst=None, description=None, *args):
        """
        :param string path: the path where the collection will be added
        :param SmapInstance inst: the containing :py:class:`SmapInstance` object
        :param string description: the contents of the sMAP description field
        :raise SmapSchemaException: if the resulting object does not validate
        """
        self.inst = inst
        setattr(self, 'path', util.norm_path(path))
        if len(args) == 1 and isinstance(args[0], dict):
            dict.__init__(self, args[0])
        else:
            self.__setitem__("Contents", [])

        if not schema.validate("Collection", self):
            raise SmapSchemaException("Error instantiating Collection: "
                                      "invalid parameter")
Ejemplo n.º 8
0
    def _lookup_r(self, id, pred=None):
        """Lookup recursively in the resource hierarchy, starting with the
        resource identifed by "id".  Returns a list of elements for which
        "pred" returns True"""
        rv = {}
        q = [id]
        root_path = getattr(self.lookup(id), 'path')

        while len(q) > 0:
            cur = self.lookup(q.pop(0))
            if ICollection.providedBy(cur):
                for child in cur['Contents']:
                    q.append(getattr(cur, 'path') + '/' + child)
            if cur and (not pred or pred(cur)):
                rvpath = util.norm_path(getattr(cur, 'path')[len(root_path):])
                rv[rvpath] = cur
        return rv
Ejemplo n.º 9
0
    def __init__(self, path, inst=None, description=None, *args):
        """
        :param string path: the path where the collection will be added
        :param SmapInstance inst: the containing :py:class:`SmapInstance` object
        :param string description: the contents of the sMAP description field
        :raise SmapSchemaException: if the resulting object does not validate
        """
        self.inst = inst
        setattr(self, 'path', util.norm_path(path))
        if len(args) == 1 and isinstance(args[0], dict):
            dict.__init__(self, args[0])
        else:
            self.__setitem__("Contents", [])

        if not schema.validate("Collection", self):
            raise SmapSchemaException("Error instantiating Collection: " 
                                      "invalid parameter")
Ejemplo n.º 10
0
def reporting_map(rpt, col_cb, ts_cb):
    q = ['/']
    while len(q) > 0:
        cur_path = util.norm_path(q.pop(0))
        cur = rpt.get(cur_path, None)
        if not cur: continue
        if 'Contents' in cur:
            for pc in cur['Contents']:
                q.append(cur_path + '/' + pc)
            col_cb(cur_path, cur)
        else:
            ts_cb(cur_path, cur)
        del rpt[cur_path]

    for p, v in rpt.iteritems():
        if 'Contents' in v:
            col_cb(p, v)
        else:
            ts_cb(p, v)
Ejemplo n.º 11
0
def reporting_map(rpt, col_cb, ts_cb):
    q = ['/']
    while len(q) > 0:
        cur_path = util.norm_path(q.pop(0))
        cur = rpt.get(cur_path, None)
        if not cur: continue
        if 'Contents' in cur:
            for pc in cur['Contents']:
                q.append(cur_path + '/' + pc)
            col_cb(cur_path, cur)
        else:
            ts_cb(cur_path, cur)
        del rpt[cur_path]

    for p, v in rpt.iteritems():
        if 'Contents' in v:
            col_cb(p, v)
        else:
            ts_cb(p, v)
Ejemplo n.º 12
0
 def __join_id(self, id):
     if util.is_string(id) and id.startswith('/'):
         return util.norm_path(self.__attach_point + '/' + id)
     else:
         return id
Ejemplo n.º 13
0
    def add_timeseries(self, path, *args, **kwargs):
        """Add a timeseries to the smap server at the given path.  This will
        generate a UUID for the timeseries.

        direct form 
        :param path a Timeseries instance
        
        simple form 
        :param args[0] is a uuid instance, or a key to generate a uuid with by combining it with the root uuid.
        :param args[1] and kwargs are arguments passed to the Timeseries constructor.  Therefore you have to include at least the UnitofMeasure

        :param boolean replace: (kwarg) replace an existing timeseries at that path instead of throwing an exception
        :param boolean recurse: recursively create parent collections instead of thrwoing an exception.  Default is True.

        :raises: :py:class:`SmapException` if the parent isn't a collection or the path already exists.
        """
        replace = kwargs.pop('replace', False)
        recurse = kwargs.pop('recurse', True)
        klass = kwargs.pop('klass', Timeseries)

        if len(args) == 0 or \
                not ITimeseries.providedBy(args[0]) and not IActuator.providedBy(args[0]):
            if len(args) == 2:
                if not isinstance(args[0], uuid.UUID):
                    id = self.uuid(args[0],
                                   namespace=kwargs.get('namespace', None))
                else:
                    id = args[0]
                args = args[1:]
            elif len(args) == 1:
                id = self.uuid(util.norm_path(path),
                               kwargs.get('namespace', None))
            else:
                id = self.uuid(util.norm_path(path))


#                 raise SmapException("SmapInstance.add_timeseries may only be called "
#                                     "with two or three arguments")

            kwargs.pop('namespace', None)
            timeseries = klass(id, *args, **kwargs)
            if id != args[0]:
                setattr(timeseries, "key", args[0])
        else:
            timeseries = args[0]

        path = util.split_path(path)
        if recurse: self._add_parents(path)
        parent = self.get_collection(util.join_path(path[:-1]))

        if not replace and util.join_path(path) in self.OBJS_PATH:
            raise SmapException("add_timeseries: path " + str(path) +
                                " exists!")
        if not parent:
            raise SmapException("add_timeseries: parent is not a collection!")
        parent.add_child(path[-1])

        # place the new timeseries into the uuid and path tables
        self.OBJS_UUID[timeseries['uuid']] = timeseries
        self.OBJS_PATH[util.join_path(path)] = timeseries
        timeseries.inst = self
        setattr(timeseries, 'path', util.join_path(path))
        if not self.loading: self.reports.update_subscriptions()
        return timeseries
Ejemplo n.º 14
0
def load(file, sections=[], **instargs):
    """Create a sMAP instance based on the representation stored in a file.

The configuration file contains sections which refer to either
reporting instances, or paths in the sMAP heirarchy.  Any section
whose name starts with ``/`` is treated as a resource name; sections
starting with ``report`` are treated as reports.

The file must contain at least one section named ``/``, which must
contain a ``uuid`` key to set the root identifier for the source.

:param string file: filename of the configuration file
:param instargs: arguments passed to the :py:class:`~smap.core.SmapInstance` constructor.
:return smap.core.SmapInstance: the created instancev
:raise smap.loader.SmapLoadError: an error is encountered processing the file
:raise smap.core.SmapError: some other error is encountered validating the loaded object 
    """
    found = None
    for l in ['', os.getcwd(), sys.prefix]:
      path = os.path.join(l, file)
      if os.path.isfile(path):
        found = path
    if not found:
      raise Exception("Config file %s not found." % file)
    print "Loading config file:", found

    conf = configobj.ConfigObj(found, indent_type='  ')

    # if there's a server section, override the default server
    # configuration with that
    if 'server' in conf:
        smapconf.SERVER = util.dict_merge(smapconf.SERVER, 
                                          dict(((k.lower(), v) for (k, v) in 
                                                conf['server'].iteritems())))
    if 'logging' in conf:
        smapconf.LOGGING = util.dict_merge(smapconf.LOGGING, 
                                           dict(((k.lower(), v) for (k, v) in 
                                                 conf['logging'].iteritems())))

    # we need the root to have a uuid
    inst = core.SmapInstance(conf['/']['uuid'], **instargs)
    inst.loading = True
    reports = []

    for s in conf:
        print "Loading section", s
        if s.startswith('report'):
            resource = conf[s].get('ReportResource', '/+')
            format = conf[s].get('Format', 'json')
            max_age = conf[s].get('MaxAge', None)
            max_age = int(max_age) if max_age != None else None

            dest = [conf[s]['ReportDeliveryLocation']]
            for i in xrange(0, 10):
                if 'ReportDeliveryLocation%i' % i in conf[s]:
                    dest.append(conf[s]['ReportDeliveryLocation%i' % i])

            reportinst = {
                'ReportDeliveryLocation' : dest,
                'ReportResource' : resource,
                'Format': format,
                'uuid' : inst.uuid(s),
                'MaxAge' : max_age,
                }
            for o in ['MinPeriod', 'MaxPeriod']:
                if o in conf[s]:
                    reportinst[o] = conf[s][o]
            for o in ['ClientCertificateFile', 'ClientPrivateKeyFile', 'CAFile']:
                if o in conf[s]:
                    reportinst[i] = os.path.expanduser(conf[s][o])

            reports.append(reportinst)
            continue
                      
        elif not s.startswith('/'):
            # path sections must start with a '/'
            # other sections might be present and could be parsed by
            # other parts of the program
            print "Warning: skipping section", s, "since it does not begin with a '/'"
            continue
        elif len(sections) and not util.norm_path(s) in sections: 
            # skip all but the listed sections if we were asked to
            continue

        s = util.norm_path(s)

        # build the UUID for the item
        props = util.build_recursive(dict(conf[s].items()))
        id = None
        if 'uuid' in conf[s]:
            key = None
            id = uuid.UUID(conf[s]['uuid'])
        elif 'key' in conf[s]:
            key = conf[s]['key']
        else:
            # default to the path if 
            key = s
        if key:
            id = inst.uuid(key)
            # raise SmapLoadError("Every config file section must have a uuid or a key!")

        # create the timeseries or collection
        if (s == '/' or 
            conf[s].get("type", None) == 'Collection' or 
            inst.get_collection(s) != None):
            if s == '/':
                c = inst.get_collection('/')
            elif inst.get_collection(s) != None:
                # sometimes you will have collections created twice,
                # for instance if a driver creates it and then we want
                # to tag it with metadata
                c = inst.get_collection(s)
            else:
                c = core.Collection(s, inst)
                inst.add_collection(s, c)
        elif conf[s].get("type", "Timeseries") == "Timeseries":
            if inst.get_timeseries(s) != None:
                c = inst.get_timeseries(s)
            else:   
                try:
                    props['Properties']['UnitofMeasure']
                except KeyError:
                    raise SmapLoadError("A Timeseries must have at least "
                                        "the Properites/UnitofMeasure key")
                
                # the Timeseries uses defaults if the conf file doesn't
                # contain the right sections.
                c = core.Timeseries(id, props['Properties']['UnitofMeasure'],
                                    data_type=props['Properties'].get('ReadingType', 
                                                                      core.Timeseries.DEFAULTS['Properties/ReadingType']),
                                    timezone=props['Properties'].get('Timezone', 
                                                                     core.Timeseries.DEFAULTS['Properties/Timezone']),
                                    buffersz=int(props.get('BufferSize', core.Timeseries.DEFAULTS['BufferSize'])))
                inst.add_timeseries(s, c)
        else:
            if not id:
                raise SmapLoadError("A driver must have a key or uuid to generate a namespace")
            
            # load a new driver manager layer
            newdrv = driver.SmapDriver.get_driver(inst, conf[s]['type'], s, id)
            # create a collection and add it at the attachment point
            c = inst.get_collection(s)
            if not c:
                c = core.Collection(s, inst)
                inst.add_collection(s, c)
            
            # Add config file specified checkers for the driver
            check = checkers.get(inst, newdrv, conf[s])
            if check:
                inst.checkers.append(check)

            # get the driver to add its points
            newdrv.setup(conf[s])

        # Metadata and Description are shared between both Collections
        # and Timeseries
        if props.has_key('Metadata'):
            # the driver may have added metadata; however config file
            # metadata overrides it
            c['Metadata'] = util.dict_merge(c.get('Metadata', {}),
                                            props['Metadata'])
        if props.has_key('Description'):
            c['Description'] = props['Description']
        if key:
            setattr(c, 'key', key)

    # since the sections could come in any order, update the reporting
    # instance to make sure all the topics are set right.
    for reportinst in reports:
        if not inst.reports.update_report(reportinst):
            inst.reports.add_report(reportinst)
    inst.reports.update_subscriptions()
    inst.loading = False
    return inst
Ejemplo n.º 15
0
def load(file, sections=[], **instargs):
    """Create a sMAP instance based on the representation stored in a file.

The configuration file contains sections which refer to either
reporting instances, or paths in the sMAP heirarchy.  Any section
whose name starts with ``/`` is treated as a resource name; sections
starting with ``report`` are treated as reports.

The file must contain at least one section named ``/``, which must
contain a ``uuid`` key to set the root identifier for the source.

:param string file: filename of the configuration file
:param instargs: arguments passed to the :py:class:`~smap.core.SmapInstance` constructor.
:return smap.core.SmapInstance: the created instancev
:raise smap.loader.SmapLoadError: an error is encountered processing the file
:raise smap.core.SmapError: some other error is encountered validating the loaded object 
    """
    found = None
    for l in ['', os.getcwd(), sys.prefix]:
        path = os.path.join(l, file)
        if os.path.isfile(path):
            found = path
    if not found:
        raise Exception("Config file %s not found." % file)
    print "Loading config file:", found

    conf = configobj.ConfigObj(found, indent_type='  ')

    # if there's a server section, override the default server
    # configuration with that
    if 'server' in conf:
        smapconf.SERVER = util.dict_merge(
            smapconf.SERVER,
            dict(((k.lower(), v) for (k, v) in conf['server'].iteritems())))

    # we need the root to have a uuid
    inst = core.SmapInstance(conf['/']['uuid'], **instargs)
    inst.loading = True
    reports = []

    for s in conf:
        print "Loading section", s
        if s.startswith('report'):
            resource = conf[s].get('ReportResource', '/+')
            format = conf[s].get('Format', 'json')
            max_age = conf[s].get('MaxAge', None)
            max_age = int(max_age) if max_age != None else None

            dest = [conf[s]['ReportDeliveryLocation']]
            for i in xrange(0, 10):
                if 'ReportDeliveryLocation%i' % i in conf[s]:
                    dest.append(conf[s]['ReportDeliveryLocation%i' % i])

            reportinst = {
                'ReportDeliveryLocation': dest,
                'ReportResource': resource,
                'Format': format,
                'uuid': inst.uuid(s),
                'MaxAge': max_age,
            }
            for o in ['MinPeriod', 'MaxPeriod']:
                if o in conf[s]:
                    reportinst[o] = conf[s][o]
            for o in [
                    'ClientCertificateFile', 'ClientPrivateKeyFile', 'CAFile'
            ]:
                if o in conf[s]:
                    reportinst[i] = os.path.expanduser(conf[s][o])

            reports.append(reportinst)
            continue

        elif not s.startswith('/'):
            # path sections must start with a '/'
            # other sections might be present and could be parsed by
            # other parts of the program
            print "Warning: skipping section", s, "since it does not begin with a '/'"
            continue
        elif len(sections) and not util.norm_path(s) in sections:
            # skip all but the listed sections if we were asked to
            continue

        s = util.norm_path(s)

        # build the UUID for the item
        props = util.build_recursive(dict(conf[s].items()))
        id = None
        if 'uuid' in conf[s]:
            key = None
            id = uuid.UUID(conf[s]['uuid'])
        elif 'key' in conf[s]:
            key = conf[s]['key']
        else:
            # default to the path if
            key = s
        if key:
            id = inst.uuid(key)
            # raise SmapLoadError("Every config file section must have a uuid or a key!")

        # create the timeseries or collection
        if (s == '/' or conf[s].get("type", None) == 'Collection'
                or inst.get_collection(s) != None):
            if s == '/':
                c = inst.get_collection('/')
            elif inst.get_collection(s) != None:
                # sometimes you will have collections created twice,
                # for instance if a driver creates it and then we want
                # to tag it with metadata
                c = inst.get_collection(s)
            else:
                c = core.Collection(s, inst)
                inst.add_collection(s, c)
        elif conf[s].get("type", "Timeseries") == "Timeseries":
            if inst.get_timeseries(s) != None:
                c = inst.get_timeseries(s)
            else:
                try:
                    props['Properties']['UnitofMeasure']
                except KeyError:
                    raise SmapLoadError("A Timeseries must have at least "
                                        "the Properites/UnitofMeasure key")

                # the Timeseries uses defaults if the conf file doesn't
                # contain the right sections.
                c = core.Timeseries(
                    id,
                    props['Properties']['UnitofMeasure'],
                    data_type=props['Properties'].get(
                        'ReadingType',
                        core.Timeseries.DEFAULTS['Properties/ReadingType']),
                    timezone=props['Properties'].get(
                        'Timezone',
                        core.Timeseries.DEFAULTS['Properties/Timezone']),
                    buffersz=int(
                        props.get('BufferSize',
                                  core.Timeseries.DEFAULTS['BufferSize'])))
                inst.add_timeseries(s, c)
        else:
            if not id:
                raise SmapLoadError(
                    "A driver must have a key or uuid to generate a namespace")

            # load a new driver manager layer
            newdrv = driver.SmapDriver.get_driver(inst, conf[s]['type'], s, id)
            # create a collection and add it at the attachment point
            c = inst.get_collection(s)
            if not c:
                c = core.Collection(s, inst)
                inst.add_collection(s, c)

            # Add config file specified checkers for the driver
            check = checkers.get(inst, newdrv, conf[s])
            if check:
                inst.checkers.append(check)

            # get the driver to add its points
            newdrv.setup(conf[s])

        # Metadata and Description are shared between both Collections
        # and Timeseries
        if props.has_key('Metadata'):
            # the driver may have added metadata; however config file
            # metadata overrides it
            c['Metadata'] = util.dict_merge(c.get('Metadata', {}),
                                            props['Metadata'])
        if props.has_key('Description'):
            c['Description'] = props['Description']
        if key:
            setattr(c, 'key', key)

    # since the sections could come in any order, update the reporting
    # instance to make sure all the topics are set right.
    for reportinst in reports:
        if not inst.reports.update_report(reportinst):
            inst.reports.add_report(reportinst)
    inst.reports.update_subscriptions()
    inst.loading = False
    return inst
Ejemplo n.º 16
0
 def __join_id(self, id):
     if util.is_string(id) and id.startswith('/'):
         return util.norm_path(self.__attach_point + '/' + id)
     else:
         return id