예제 #1
0
class ServiceConfigure(object):
    '''
    ServiceConfigure is meant to be a generic class for any service
    which registers itself to, and gets configuration from UNIS. It
    was originally developed for BLiPP, but BLiPP specific features
    should be in the BlippConfigure class which extends
    ServiceConfigure.
    '''
    def __init__(self, initial_config={}, node_id=None, urn=None):
        if not node_id:
            node_id = settings.UNIS_ID
        self.node_id = node_id
        self.urn = urn
        self.config = initial_config
        self.unis = UNISInstance(self.config)
        self.service_setup = False

    def initialize(self):
        self._setup_node(self.node_id)
        self._setup_service()

    def refresh(self):
        r = self.unis.get("/services/" + self.config["id"])
        if not r:
            logger.warn('refresh', msg="refresh failed")
        else:
            self.config = r

    def _setup_node(self, node_id):
        config = self.config
        logger.debug('_setup_node', config=pprint.pformat(config))
        hostname = settings.HOSTNAME
        urn = settings.HOST_URN if not self.urn else self.urn
        if node_id:
            r = self.unis.get("/nodes/" + str(node_id))
            if not r:
                logger.warn('_setup_node', msg="node id %s not found" % node_id)
                node_id = None
        if not node_id:
            r = self.unis.get("/nodes?urn=" + urn)
            if r and len(r):
                r = r[0]
                logger.info('_setup_node',
                            msg="found node with our URN and id %s" % r["id"])
            else:
                r = self.unis.post("/nodes",
                              data={
                        "$schema": settings.SCHEMAS["nodes"],
                        "name": hostname,
                        "urn": urn})
            if r:
                self.node_id = r["id"]
        if r:
            config["runningOn"] = {
                "href": r["selfRef"],
                "rel": "full"}
            self.node_setup = True
        else:
            config["runningOn"] = {"href": ""}
            logger.warn('_setup_node', msg="Unable to set up node in UNIS")

    def _setup_service(self):
        config = self.config
        logger.debug('_setup_service', config=pprint.pformat(config))
        r = None
        if config.get("id", None):
            r = self.unis.get("/services/" + config["id"])
        if not r:
            logger.warn('_setup_service',
                        msg="service id not specified or not found "\
                            "unis instance ...querying for service")
            rlist = self.unis.get("/services?name=" + config.get("name", None) +\
                                      "&runningOn.href=" + config["runningOn"]["href"] + "&limit=2")
            # loop over the returned services and find one that
            # doesn't return 410 see
            # https://uisapp2.iu.edu/jira-prd/browse/GEMINI-98
            if rlist:
                for i in range(len(rlist)):
                    r = self.unis.get('/services/' + rlist[i]["id"])
                    if r:
                        if isinstance(r, list):
                            logger.warn('_setup_service',
                                        msg="id not unique... taking first result")
                            r = r[0]
                        logger.info('_setup_service',
                                    msg="%s service found with id %s" % (config["name"], r["id"]))
                        break
            else:
                logger.warn('_setup_service',
                            msg="no service found by id or querying "\
                                "...creating new service")

        if r:
            merge_dicts(config, r)

        # always update UNIS with the merged config
        if config.get("id", None):
            r = self.unis.put("/services/" + config["id"], data=config)
        else:
            r = self.unis.post("/services", data=config)
        if r:
            merge_dicts(config, r)

        if r:
            self.service_setup = True
        else:
            logger.warn('_setup_service', msg="unable to set up service in UNIS")


    def get(self, key, default=None):
        try:
            return self.config[key]
        except KeyError:
            return default

    def __getitem__(self, key):
        '''
        This allows an object which is an instance of this class to behave
        like a dictionary when queried with [] syntax
        '''
        return self.config[key]
예제 #2
0
class Collector:
    """Collects reported measurements and aggregates them for
    sending to MS at appropriate intervals.

    Also does a bunch of other stuff which should probably be handled by separate classes.
    Creates all the metadata objects, and the measurement object in UNIS for all data inserted.
    Depends directly on the MS and UNIS... output could be far more modular.
    """

    def __init__(self, service, measurement):
        self.config = measurement["configuration"]
        self.service = service
        self.measurement = measurement
        self.collections_created = False
        self.ms = MSInstance(service, measurement)
        self.dl = DataLogger(service, measurement)
        self.mids = {} # {subject1: {metric1:mid, metric2:mid}, subj2: {...}}
        # {mid: [{"ts": ts, "value": val}, {"ts": ts, "value": val}]}
        self.mid_to_data = {}
        self.mid_to_et = {}
        self.unis = UNISInstance(service)
        self.num_collected = 0

    def insert(self, data, ts):
        '''
        Called (by probe_runner) to insert new data into this collector object.
        '''
        mids = self.mids
        for subject, met_val in data.iteritems():
            if "ts" in met_val:
                ts = met_val["ts"]
                del met_val["ts"]
            for metric, value in met_val.iteritems():
                if metric not in self.measurement["eventTypes"]:
                    self._add_et(metric)
                if not metric in mids.get(subject, {}):
                    r = self.unis.find_or_create_metadata(subject,
                                                          metric,
                                                          self.measurement)
                    mids.setdefault(subject, {})[metric] = r["id"]
                    self.mid_to_data[r["id"]] = []
                self.mid_to_et[mids[subject][metric]] = metric
                self._insert_datum(mids[subject][metric], ts, value)

        self.num_collected += 1
        if self.num_collected >= self.config["reporting_params"]:
            ret = self.report()
            if ret:
                self.num_collected = 0

    def _insert_datum(self, mid, ts, val):
        item = dict({"ts": ts * 10e5,
                     "value":val})
        self.mid_to_data[mid].append(item)

    def _add_et(self, metric):
        self.measurement["eventTypes"].append(metric)
        r = self.unis.put("/measurements/" +
                          self.measurement["id"],
                          data=self.measurement)
        if r:
            self.measurement = r

    def report(self):
        '''
        Send all data collected so far, then clear stored data.
        '''
        post_data = []
        for mid, data in self.mid_to_data.iteritems():
            if len(data):
                post_data.append({"mid":mid, "data":data})
        ms_ret = self.ms.post_data(post_data)
        dl_ret = self.dl.write_data(post_data, self.mid_to_et)
        if not ms_ret and not dl_ret and self.num_collected < self.config["reporting_tolerance"] * self.config["reporting_params"]:
            return None
        self._clear_data()
        return True

    def _clear_data(self):
        for mid in self.mid_to_data:
            self.mid_to_data[mid]=[]