def run_event_server(ftrack_url, ftrack_user, ftrack_api_key,
                     ftrack_events_path, no_stored_credentials,
                     store_credentials, legacy, clockify_api_key,
                     clockify_workspace):
    if not no_stored_credentials:
        cred = credentials.get_credentials(ftrack_url)
        username = cred.get('username')
        api_key = cred.get('api_key')

    if clockify_workspace and clockify_api_key:
        os.environ["CLOCKIFY_WORKSPACE"] = clockify_workspace
        os.environ["CLOCKIFY_API_KEY"] = clockify_api_key

    # Check url regex and accessibility
    ftrack_url = check_ftrack_url(ftrack_url)
    if not ftrack_url:
        print('Exiting! < Please enter Ftrack server url >')
        return 1

    # Validate entered credentials
    if not validate_credentials(ftrack_url, username, api_key):
        print('Exiting! < Please enter valid credentials >')
        return 1

    if store_credentials:
        credentials.save_credentials(username, api_key, ftrack_url)

    # Set Ftrack environments
    os.environ["FTRACK_SERVER"] = ftrack_url
    os.environ["FTRACK_API_USER"] = username
    os.environ["FTRACK_API_KEY"] = api_key
    # TODO This won't work probably
    if ftrack_events_path:
        if isinstance(ftrack_events_path, (list, tuple)):
            ftrack_events_path = os.pathsep.join(ftrack_events_path)
        os.environ["FTRACK_EVENTS_PATH"] = ftrack_events_path

    if legacy:
        return legacy_server(ftrack_url)

    return main_loop(ftrack_url)
Beispiel #2
0
def main(argv):
    '''
    There are 4 values neccessary for event server:
    1.) Ftrack url - "studio.ftrackapp.com"
    2.) Username - "my.username"
    3.) API key - "apikey-long11223344-6665588-5565"
    4.) Path/s to events - "X:/path/to/folder/with/events"

    All these values can be entered with arguments or environment variables.
    - arguments:
        "-ftrackurl {url}"
        "-ftrackuser {username}"
        "-ftrackapikey {api key}"
        "-ftrackeventpaths {path to events}"
    - environment variables:
        FTRACK_SERVER
        FTRACK_API_USER
        FTRACK_API_KEY
        FTRACK_EVENTS_PATH

    Credentials (Username & API key):
    - Credentials can be stored for auto load on next start
    - To *Store/Update* these values add argument "-storecred"
        - They will be stored to appsdir file when login is successful
    - To *Update/Override* values with enviromnet variables is also needed to:
        - *don't enter argument for that value*
        - add argument "-noloadcred" (currently stored credentials won't be loaded)

    Order of getting values:
        1.) Arguments are always used when entered.
            - entered values through args have most priority! (in each case)
        2.) Credentials are tried to load from appsdir file.
            - skipped when credentials were entered through args or credentials
                are not stored yet
            - can be skipped with "-noloadcred" argument
        3.) Environment variables are last source of values.
            - will try to get not yet set values from environments

    Best practice:
    - set environment variables FTRACK_SERVER & FTRACK_EVENTS_PATH
    - launch event_server_cli with args:
    ~/event_server_cli.py -ftrackuser "{username}" -ftrackapikey "{API key}" -storecred
    - next time launch event_server_cli.py only with set environment variables
        FTRACK_SERVER & FTRACK_EVENTS_PATH
    '''
    parser = argparse.ArgumentParser(description='Ftrack event server')
    parser.add_argument("-ftrackurl",
                        type=str,
                        metavar='FTRACKURL',
                        help=("URL to ftrack server where events should handle"
                              " (default from environment: $FTRACK_SERVER)"))
    parser.add_argument(
        "-ftrackuser",
        type=str,
        help=("Username should be the username of the user in ftrack"
              " to record operations against."
              " (default from environment: $FTRACK_API_USER)"))
    parser.add_argument("-ftrackapikey",
                        type=str,
                        help=("Should be the API key to use for authentication"
                              " (default from environment: $FTRACK_API_KEY)"))
    parser.add_argument(
        "-ftrackeventpaths",
        nargs='+',
        help=("List of paths where events are stored."
              " (default from environment: $FTRACK_EVENTS_PATH)"))
    parser.add_argument('-storecred',
                        help=("Entered credentials will be also stored"
                              " to apps dir for future usage"),
                        action="store_true")
    parser.add_argument('-noloadcred',
                        help="Load creadentials from apps dir",
                        action="store_true")
    parser.add_argument('-legacy',
                        help="Load creadentials from apps dir",
                        action="store_true")
    ftrack_url = os.environ.get('FTRACK_SERVER')
    username = os.environ.get('FTRACK_API_USER')
    api_key = os.environ.get('FTRACK_API_KEY')
    event_paths = os.environ.get('FTRACK_EVENTS_PATH')

    kwargs, args = parser.parse_known_args(argv)

    if kwargs.ftrackurl:
        ftrack_url = kwargs.ftrackurl

    if kwargs.ftrackeventpaths:
        event_paths = kwargs.ftrackeventpaths

    if not kwargs.noloadcred:
        cred = credentials.get_credentials(ftrack_url)
        username = cred.get('username')
        api_key = cred.get('api_key')

    if kwargs.ftrackuser:
        username = kwargs.ftrackuser

    if kwargs.ftrackapikey:
        api_key = kwargs.ftrackapikey

    legacy = kwargs.legacy
    # Check url regex and accessibility
    ftrack_url = check_ftrack_url(ftrack_url)
    if not ftrack_url:
        print('Exiting! < Please enter Ftrack server url >')
        return 1

    # Validate entered credentials
    if not validate_credentials(ftrack_url, username, api_key):
        print('Exiting! < Please enter valid credentials >')
        return 1

    # Process events path
    event_paths, not_found = process_event_paths(event_paths)
    if not_found:
        print('WARNING: These paths were not found: {}'.format(str(not_found)))
    if not event_paths:
        if not_found:
            print('ERROR: Any of entered paths is valid or can be accesible.')
        else:
            print('ERROR: Paths to events are not set. Exiting.')
        return 1

    if kwargs.storecred:
        credentials.save_credentials(username, api_key, ftrack_url)

    # Set Ftrack environments
    os.environ["FTRACK_SERVER"] = ftrack_url
    os.environ["FTRACK_API_USER"] = username
    os.environ["FTRACK_API_KEY"] = api_key
    os.environ["FTRACK_EVENTS_PATH"] = event_paths

    if legacy:
        return legacy_server(ftrack_url)

    return main_loop(ftrack_url)
Beispiel #3
0
def main_loop(ftrack_url):
    """ This is main loop of event handling.

    Loop is handling threads which handles subprocesses of event storer and
    processor. When one of threads is stopped it is tested to connect to
    ftrack and mongo server. Threads are not started when ftrack or mongo
    server is not accessible. When threads are started it is checked for socket
    signals as heartbeat. Heartbeat must become at least once per 30sec
    otherwise thread will be killed.
    """

    os.environ["FTRACK_EVENT_SUB_ID"] = str(uuid.uuid1())
    # Get mongo hostname and port for testing mongo connection

    mongo_uri, mongo_port, database_name, collection_name = (
        get_ftrack_event_mongo_info())

    # Current file
    file_path = os.path.dirname(os.path.realpath(__file__))

    min_fail_seconds = 5
    max_fail_count = 3
    wait_time_after_max_fail = 10

    # Threads data
    storer_name = "StorerThread"
    storer_port = 10001
    storer_path = "{}/sub_event_storer.py".format(file_path)
    storer_thread = None
    storer_last_failed = datetime.datetime.now()
    storer_failed_count = 0

    processor_name = "ProcessorThread"
    processor_port = 10011
    processor_path = "{}/sub_event_processor.py".format(file_path)
    processor_thread = None
    processor_last_failed = datetime.datetime.now()
    processor_failed_count = 0

    statuser_name = "StorerThread"
    statuser_port = 10021
    statuser_path = "{}/sub_event_status.py".format(file_path)
    statuser_thread = None
    statuser_last_failed = datetime.datetime.now()
    statuser_failed_count = 0

    ftrack_accessible = False
    mongo_accessible = False

    printed_ftrack_error = False
    printed_mongo_error = False

    # stop threads on exit
    # TODO check if works and args have thread objects!
    def on_exit(processor_thread, storer_thread, statuser_thread):
        if processor_thread is not None:
            processor_thread.stop()
            processor_thread.join()
            processor_thread = None

        if storer_thread is not None:
            storer_thread.stop()
            storer_thread.join()
            storer_thread = None

        if statuser_thread is not None:
            statuser_thread.stop()
            statuser_thread.join()
            statuser_thread = None

    atexit.register(on_exit,
                    processor_thread=processor_thread,
                    storer_thread=storer_thread,
                    statuser_thread=statuser_thread)

    system_name, pc_name = platform.uname()[:2]
    host_name = socket.gethostname()
    main_info = {
        "created_at": datetime.datetime.now().strftime("%Y.%m.%d %H:%M:%S"),
        "Username": getpass.getuser(),
        "Host Name": host_name,
        "Host IP": socket.gethostbyname(host_name)
    }
    main_info_str = json.dumps(main_info)
    # Main loop
    while True:
        # Check if accessible Ftrack and Mongo url
        if not ftrack_accessible:
            ftrack_accessible = check_ftrack_url(ftrack_url)

        if not mongo_accessible:
            mongo_accessible = check_mongo_url(mongo_uri, mongo_port)

        # Run threads only if Ftrack is accessible
        if not ftrack_accessible or not mongo_accessible:
            if not mongo_accessible and not printed_mongo_error:
                print("Can't access Mongo {}".format(mongo_uri))

            if not ftrack_accessible and not printed_ftrack_error:
                print("Can't access Ftrack {}".format(ftrack_url))

            if storer_thread is not None:
                storer_thread.stop()
                storer_thread.join()
                storer_thread = None

            if processor_thread is not None:
                processor_thread.stop()
                processor_thread.join()
                processor_thread = None

            printed_ftrack_error = True
            printed_mongo_error = True

            time.sleep(1)
            continue

        printed_ftrack_error = False
        printed_mongo_error = False

        # ====== STATUSER =======
        if statuser_thread is None:
            if statuser_failed_count < max_fail_count:
                statuser_thread = socket_thread.StatusSocketThread(
                    statuser_name, statuser_port, statuser_path,
                    [main_info_str])
                statuser_thread.start()

            elif statuser_failed_count == max_fail_count:
                print(("Statuser failed {}times in row"
                       " I'll try to run again {}s later").format(
                           str(max_fail_count), str(wait_time_after_max_fail)))
                statuser_failed_count += 1

            elif ((datetime.datetime.now() - statuser_last_failed).seconds >
                  wait_time_after_max_fail):
                statuser_failed_count = 0

        # If thread failed test Ftrack and Mongo connection
        elif not statuser_thread.isAlive():
            statuser_thread.join()
            statuser_thread = None
            ftrack_accessible = False
            mongo_accessible = False

            _processor_last_failed = datetime.datetime.now()
            delta_time = (_processor_last_failed -
                          statuser_last_failed).seconds

            if delta_time < min_fail_seconds:
                statuser_failed_count += 1
            else:
                statuser_failed_count = 0
            statuser_last_failed = _processor_last_failed

        elif statuser_thread.stop_subprocess:
            print("Main process was stopped by action")
            on_exit(processor_thread, storer_thread, statuser_thread)
            os.kill(os.getpid(), signal.SIGTERM)
            return 1

        # ====== STORER =======
        # Run backup thread which does not requeire mongo to work
        if storer_thread is None:
            if storer_failed_count < max_fail_count:
                storer_thread = socket_thread.SocketThread(
                    storer_name, storer_port, storer_path)
                storer_thread.start()

            elif storer_failed_count == max_fail_count:
                print(("Storer failed {}times I'll try to run again {}s later"
                       ).format(str(max_fail_count),
                                str(wait_time_after_max_fail)))
                storer_failed_count += 1
            elif ((datetime.datetime.now() - storer_last_failed).seconds >
                  wait_time_after_max_fail):
                storer_failed_count = 0

        # If thread failed test Ftrack and Mongo connection
        elif not storer_thread.isAlive():
            if storer_thread.mongo_error:
                raise MongoPermissionsError()
            storer_thread.join()
            storer_thread = None
            ftrack_accessible = False
            mongo_accessible = False

            _storer_last_failed = datetime.datetime.now()
            delta_time = (_storer_last_failed - storer_last_failed).seconds
            if delta_time < min_fail_seconds:
                storer_failed_count += 1
            else:
                storer_failed_count = 0
            storer_last_failed = _storer_last_failed

        # ====== PROCESSOR =======
        if processor_thread is None:
            if processor_failed_count < max_fail_count:
                processor_thread = socket_thread.SocketThread(
                    processor_name, processor_port, processor_path)
                processor_thread.start()

            elif processor_failed_count == max_fail_count:
                print(("Processor failed {}times in row"
                       " I'll try to run again {}s later").format(
                           str(max_fail_count), str(wait_time_after_max_fail)))
                processor_failed_count += 1

            elif ((datetime.datetime.now() - processor_last_failed).seconds >
                  wait_time_after_max_fail):
                processor_failed_count = 0

        # If thread failed test Ftrack and Mongo connection
        elif not processor_thread.isAlive():
            if processor_thread.mongo_error:
                raise Exception(
                    "Exiting because have issue with acces to MongoDB")
            processor_thread.join()
            processor_thread = None
            ftrack_accessible = False
            mongo_accessible = False

            _processor_last_failed = datetime.datetime.now()
            delta_time = (_processor_last_failed -
                          processor_last_failed).seconds

            if delta_time < min_fail_seconds:
                processor_failed_count += 1
            else:
                processor_failed_count = 0
            processor_last_failed = _processor_last_failed

        if statuser_thread is not None:
            statuser_thread.set_process("storer", storer_thread)
            statuser_thread.set_process("processor", processor_thread)

        time.sleep(1)
Beispiel #4
0
def legacy_server(ftrack_url):
    # Current file
    file_path = os.path.dirname(os.path.realpath(__file__))

    min_fail_seconds = 5
    max_fail_count = 3
    wait_time_after_max_fail = 10

    subproc = None
    subproc_path = "{}/sub_legacy_server.py".format(file_path)
    subproc_last_failed = datetime.datetime.now()
    subproc_failed_count = 0

    ftrack_accessible = False
    printed_ftrack_error = False

    while True:
        if not ftrack_accessible:
            ftrack_accessible = check_ftrack_url(ftrack_url)

        # Run threads only if Ftrack is accessible
        if not ftrack_accessible and not printed_ftrack_error:
            print("Can't access Ftrack {} <{}>".format(
                ftrack_url, str(datetime.datetime.now())))
            if subproc is not None:
                if subproc.poll() is None:
                    subproc.terminate()

                subproc = None

            printed_ftrack_error = True

            time.sleep(1)
            continue

        printed_ftrack_error = False

        if subproc is None:
            if subproc_failed_count < max_fail_count:
                subproc = subprocess.Popen(["python", subproc_path],
                                           stdout=subprocess.PIPE)
            elif subproc_failed_count == max_fail_count:
                print(("Storer failed {}times I'll try to run again {}s later"
                       ).format(str(max_fail_count),
                                str(wait_time_after_max_fail)))
                subproc_failed_count += 1
            elif ((datetime.datetime.now() - subproc_last_failed).seconds >
                  wait_time_after_max_fail):
                subproc_failed_count = 0

        # If thread failed test Ftrack and Mongo connection
        elif subproc.poll() is not None:
            subproc = None
            ftrack_accessible = False

            _subproc_last_failed = datetime.datetime.now()
            delta_time = (_subproc_last_failed - subproc_last_failed).seconds
            if delta_time < min_fail_seconds:
                subproc_failed_count += 1
            else:
                subproc_failed_count = 0
            subproc_last_failed = _subproc_last_failed

        time.sleep(1)