示例#1
0
def _update_transaction_id(object_model, id_list=None):
    """
    Updates the in-memory transaction dict when object_models are updated.

    Arguments:
    id_list -- A list of <object_model>_ids

    Returns:
    None
    """
    if id_list is not None:
        trans = flask.current_app.transactions[object_model]
        trans_time = time.time()
        lock_name = '%s-txid' % object_model

        utility.lock_acquire(lock_name)

        try:
            while trans_time in trans:
                trans_time += 0.000001

            trans[trans_time] = set(id_list)
        except:
            utility.lock_release(lock_name)
            raise

        utility.lock_release(lock_name)

        # prune any updates > 5 min ago
        trans_time_past = trans_time - (60 * 5)
        for k in [x for x in trans.keys() if x < trans_time_past]:
            del trans[k]

        semaphore_name = '%s-changes' % object_model
        utility.notify(semaphore_name)
示例#2
0
def task_by_id(object_id):
    result = generic.object_by_id(object_type, object_id)

    if flask.request.method == "PUT":
        api = api_from_models()
        task = api.task_get_by_id(object_id)
        if "node_id" in task:
            flask.current_app.logger.debug("Task: %s" % task)
            task_semaphore = "task-for-%s" % task["node_id"]
            flask.current_app.logger.debug("notifying event %s" % task_semaphore)
            utility.notify(task_semaphore)

    return result
示例#3
0
def list():
    _clean_tasks()

    result = generic.list(object_type)

    if flask.request.method == "POST":
        data = flask.request.json
        if "node_id" in data:
            task_semaphore = "task-for-%s" % data["node_id"]
            flask.current_app.logger.debug("notifying event %s" % task_semaphore)
            utility.notify(task_semaphore)

    return result
示例#4
0
def task_by_id(object_id):
    result = generic.object_by_id(object_type, object_id)

    if flask.request.method == 'PUT':
        api = api_from_models()
        task = api.task_get_by_id(object_id)
        if 'node_id' in task:
            flask.current_app.logger.debug('Task: %s' % task)
            task_semaphore = 'task-for-%s' % task['node_id']
            flask.current_app.logger.debug('notifying event %s' %
                                           task_semaphore)
            utility.notify(task_semaphore)

    return result
示例#5
0
def list():
    _clean_tasks()

    result = generic.list(object_type)

    if flask.request.method == 'POST':
        data = flask.request.json
        if 'node_id' in data:
            task_semaphore = 'task-for-%s' % data['node_id']
            flask.current_app.logger.debug('notifying event %s' %
                                           task_semaphore)
            utility.notify(task_semaphore)

    return result
示例#6
0
def _notify(updated_object, object_type, object_id):
    semaphore = '%s-id-%s' % (object_type, object_id)
    utility.notify(semaphore)





    # TODO: Generalize the block of code with a TODO below here.
#    if updated_object is not None and object_type in related_notifications:
#        for field, entity in related_notifications[object_type].iteritems():
#            if field in updated_object and updated_object[field] is not None:
#                semaphore = '%s-id-%s' % (entity, updated_object[field])
#                utility.notify(semaphore)

    # TODO (wilk or rpedde): Use specific notifications for inheritance
    if object_type not in ('attrs', 'facts', 'nodes'):
        return
    try:
        node_id = updated_object['node_id']
        node = None
    except KeyError:
        node_id = updated_object['id']
        node = updated_object
    if object_type != "attrs":
        api = api_from_models()
        # We're just going to notify every child when containers are updated
        if node is None:
            try:
                node = api._model_get_by_id('nodes', node_id)
            except (exceptions.IdNotFound):
                return

        if 'container' in node['facts'].get('backends', []):
            children = utility.get_direct_children(node, api)
            for child in children:
                semaphore = 'nodes-id-%s' % child['id']
                utility.notify(semaphore)
        # Update transaction for node and children
        id_list = utility.fully_expand_nodelist([node], api)
        # TODO(shep): this needs to be better abstracted
    # Need a codepath to update transaction for attr modifications
    else:
        # TODO(shep): this needs to be better abstracted
        id_list = [node_id]
    _update_transaction_id('nodes', id_list)
示例#7
0
def task_log(task_id):
    """
    Tail a logfile on a client.  Given a task id, this asks
    the client that ran it grab the last 1k of the logs from
    that task and push them at the server on an ephemeral port.

    This gets returned as 'log' in the return json blob.
    """

    api = api_from_models()
    try:
        task = api._model_get_by_id('tasks', task_id)
    except exceptions.IdNotFound:
        return generic.http_notfound(msg='Task %s not found' % task_id)

    watching = flask.request.args.get('watch', False) is not False

    offset_raw = flask.request.args.get('offset', '1024')
    offset = {}
    try:
        offset['length'] = int(offset_raw)
    except ValueError:
        return generic.http_badrequest(msg='Offset must be an integer.')

    if offset_raw.startswith('+'):
        offset['position'] = 'start'
    else:
        offset['position'] = 'end'

    s = gevent.socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(('', 0))
    s.listen(1)

    addr, port = s.getsockname()
    if addr == '0.0.0.0':
        # we need something more specific.  This is
        # pretty naive, but works.  If we could get the
        # flask fd, we could getsockname on that side
        # and see what we requested on.  that would
        # likely be a better guess.

        # generate a list of all interfaces with
        # non-loopback ipv4 addresses.
        addr = None

        addrs = {}
        for iface in netifaces.interfaces():
            if netifaces.AF_INET in netifaces.ifaddresses(iface):
                for ablock in netifaces.ifaddresses(iface)[netifaces.AF_INET]:
                    if not iface in addrs:
                        addrs[iface] = []
                    if not ablock['addr'].startswith('127'):
                        addrs[iface].append(ablock['addr'])

                if iface in addrs and len(addrs[iface]) == 0:
                    addrs.pop(iface)

        if len(addrs) == 0:
            s.close()
            return generic.http_badrequest(msg='cannot determine interface')

        # try least-to-most interesting
        for iface in ['en1', 'en0', 'eth1', 'eth0']:
            if iface in addrs:
                addr = addrs[iface][0]

        # just grab the first
        if not addr:
            addr = addrs[addrs.keys().pop(0)][0]

    client_action = 'logfile.watch' if watching else 'logfile.tail'

    payload = {'node_id': task['node_id'],
               'action': client_action,
               'payload': {'task_id': task['id'],
                           'dest_ip': addr,
                           'dest_port': port,
                           'offset': offset}}
    if watching:
        payload['payload']['timeout'] = 30

    new_task = api._model_create('tasks', payload)

    # this should really be done by the data model.  <sigh>
    task_semaphore = 'task-for-%s' % task['node_id']
    flask.current_app.logger.debug('notifying event %s' %
                                   task_semaphore)
    utility.notify(task_semaphore)

    # # force the wake
    # utility.sleep(0.1)

    # now wait for a few seconds to see if we get the
    # connection
    s.settimeout(10)

    try:
        conn, addr = s.accept()
    except socket.timeout:
        flask.current_app.logger.error('Error waiting for '
                                       'client connect on log tail')

        s.close()
        api._model_update_by_id('tasks', new_task['id'],
                                {'state': 'cancelled'})

        return generic.http_notfound(msg='cannot fetch logs')

    if watching:
        watch = str(uuid.uuid1())
        watched_tasks[watch] = {
            'socket': conn,
            'time': time.time(),
            'task_id': new_task['id'],
            'notifier': gevent.event.Event(),
            'accept_socket': s,
            'event': gevent.spawn(
                lambda: _serve_connection(
                    api, watch))}

        return generic.http_response(200, request=watch)

    # otherwise, just tail
    data = _generate_data(conn, s)

    return flask.Response(data, mimetype='text/plain')
示例#8
0
def task_log(task_id):
    """
    Tail a logfile on a client.  Given a task id, this asks
    the client that ran it grab the last 1k of the logs from
    that task and push them at the server on an ephemeral port.

    This gets returned as 'log' in the return json blob.
    """

    api = api_from_models()
    try:
        task = api._model_get_by_id("tasks", task_id)
    except exceptions.IdNotFound:
        return generic.http_notfound(msg="Task %s not found" % task_id)

    watching = flask.request.args.get("watch", False) is not False

    offset = flask.request.args.get("offset", 1024)
    try:
        offset = int(offset)
    except ValueError:
        pass
    if not isinstance(offset, int) or offset < 0:
        message = "Offset must be a non-negative integer"
        return generic.http_badrequest(msg=message)

    s = gevent.socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(("", 0))
    s.listen(1)

    addr, port = s.getsockname()
    if addr == "0.0.0.0":
        # we need something more specific.  This is
        # pretty naive, but works.  If we could get the
        # flask fd, we could getsockname on that side
        # and see what we requested on.  that would
        # likely be a better guess.

        # generate a list of all interfaces with
        # non-loopback ipv4 addresses.
        addr = None

        addrs = {}
        for iface in netifaces.interfaces():
            if netifaces.AF_INET in netifaces.ifaddresses(iface):
                for ablock in netifaces.ifaddresses(iface)[netifaces.AF_INET]:
                    if not iface in addrs:
                        addrs[iface] = []
                    if not ablock["addr"].startswith("127"):
                        addrs[iface].append(ablock["addr"])

                if iface in addrs and len(addrs[iface]) == 0:
                    addrs.pop(iface)

        if len(addrs) == 0:
            s.close()
            return generic.http_badrequest(msg="cannot determine interface")

        # try least-to-most interesting
        for iface in ["en1", "en0", "eth1", "eth0"]:
            if iface in addrs:
                addr = addrs[iface][0]

        # just grab the first
        if not addr:
            addr = addrs[addrs.keys().pop(0)][0]

    client_action = "logfile.watch" if watching else "logfile.tail"

    payload = {
        "node_id": task["node_id"],
        "action": client_action,
        "payload": {"task_id": task["id"], "dest_ip": addr, "dest_port": port, "offset": offset},
    }
    if watching:
        payload["payload"]["timeout"] = 30

    new_task = api._model_create("tasks", payload)

    # this should really be done by the data model.  <sigh>
    task_semaphore = "task-for-%s" % task["node_id"]
    flask.current_app.logger.debug("notifying event %s" % task_semaphore)
    utility.notify(task_semaphore)

    # force the wake
    utility.sleep(0.1)

    # now wait for a few seconds to see if we get the
    # connection
    s.settimeout(10)

    try:
        conn, addr = s.accept()
    except socket.timeout:
        flask.current_app.logger.error("Error waiting for " "client connect on log tail")

        s.close()
        api._model_update_by_id("tasks", new_task["id"], {"state": "cancelled"})

        return generic.http_notfound(msg="cannot fetch logs")

    if watching:
        watch = str(uuid.uuid1())
        watched_tasks[watch] = {
            "socket": conn,
            "time": time.time(),
            "task_id": new_task["id"],
            "notifier": gevent.event.Event(),
            "accept_socket": s,
            "event": gevent.spawn(lambda: _serve_connection(api, watch)),
        }

        return generic.http_response(200, request=watch)

    # otherwise, just tail
    data = _generate_data(conn, s)

    return flask.Response(data, mimetype="text/plain")