Exemple #1
0
    def close(self):
        if self.loop is not None:
            self.loop.close()

        if self.sckt is not None:
            info('Shutting down server')
            self.sckt.close()
Exemple #2
0
    def new_trial(self, trial: Trial):
        try:
            self.cursor.execute(
                """
                INSERT INTO
                    track.trials (uid, hash, revision, name, description,
                    tags, metadata, metrics, version, project_id, group_id, parameters, status)
                VALUES
                    (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s);
                """,
                (trial.uid, trial.hash, trial.revision, trial.name,
                 trial.description, self.serialize(trial.tags),
                 self.serialize(trial.metadata), self.serialize(
                     trial.metrics), self.encode_version(
                         trial.version), self.encode_uid(trial.project_id),
                 self.encode_uid(
                     trial.group_id), self.serialize(
                         trial.parameters), self.serialize(trial.status)))
            return trial

        except psycopg2.errors.UniqueViolation:
            trial.revision += 1
            info(
                f'Trial already exist increasing revision (rev: {trial.revision})'
            )
            return self.new_trial(trial)
Exemple #3
0
    def set_trial(self,
                  trial: Optional[Trial] = None,
                  force: bool = False,
                  **kwargs):
        """Set a new trial

        Parameters
        ----------
        trial: Optional[Trial]
            project definition you can use to create or set the project

        force: bool
            by default once the trial is set it cannot be changed.
            use force to override this behaviour.

        kwargs: {uid, hash, revision}
            arguments used to create a `Trial` object if no Trial object were provided.
            You should specify `uid` or the pair `(hash, revision)`.
            See :func:`~track.structure.Trial` for possible arguments

        Returns
        -------
        returns a trial logger
        """
        if self.trial is not None and not force:
            info('Trial is already set, to override use force=True')
            return self.logger

        if trial is None:
            uhash = kwargs.pop('hash', None)
            uid = kwargs.pop('uid', None)
            version = kwargs.pop('version', self.version())

            trial = Trial(version=version, **kwargs)
            if uhash is not None:
                trial.hash = uhash

            if uid is not None:
                trial.uid = uid

        try:
            if trial.version is None:
                trial.version = self.version()

            trials = self.protocol.get_trial(trial)

            if trials is None:
                raise TrialDoesNotExist(
                    f'Trial (hash: {trial.hash}, v:{trial.version} rev: {trial.revision}) does not exist!'
                )

            self.trial = trials[0]
            self.logger = TrialLogger(self.trial, self.protocol)
            return self.logger

        except IndexError:
            raise TrialDoesNotExist(
                f'cannot set trial (id: {trial.uid}, hash:{hash}) it does not exist'
            )
Exemple #4
0
    def _new_trial(self, force=False, parameters=None, **kwargs):
        """Create a new trial if all the required arguments are satisfied.

        To provide a better user experience if not all arguments are provided a delayed trials is created
        that holds all the data provided and will create the trial once all arguments are ready.
        Currently only `arguments` i.e the parameters of the experience is required. This is
        because they are needed to compute the trial uid (which is a hash of the parameters).

        If no project is set, the trial is inserted in a catch all project named `orphan`

        Parameters
        ----------
        force: bool
            by default once the trial is set it cannot be changed.
            use force to override this behaviour.

        kwargs
            See :func:`~track.structure.Trial` for possible arguments

        Returns
        -------
        returns a trial
        """
        if isinstance(parameters, Namespace):
            parameters = dict(**vars(parameters))

        if self.trial is not None and not is_delayed_call(
                self.trial) and not force:
            info(f'Trial is already set, to override use force=True')
            return self.trial

        # if arguments are not specified do not create the trial just yet
        # wait for the user to be able to specify the parameters so we can have a meaningful hash
        if parameters is None:
            if is_delayed_call(self.trial):
                raise RuntimeError('Trial needs parameters')

            self.trial = delay_call(self._new_trial, **kwargs)
            # return the logger with the delayed trial
            return self.trial

        # replace the trial or delayed trial by its actual value
        if parameters or is_delayed_call(self.trial):
            self.trial = self._make_trial(parameters=parameters, **kwargs)

        assert self.trial is not None, f'Trial cant be None because {parameters} or {is_delayed_call(self.trial)}'

        if self.project is None:
            self.project = self.set_project(name='orphan')

        assert self.project is not None, 'Project cant be None'

        self.protocol.add_project_trial(self.project, self.trial)

        if self.group is not None:
            self.protocol.add_group_trial(self.group, self.trial)

        return self.trial
Exemple #5
0
    def new_trial(self, trial: Trial, auto_increment=None):
        try:
            self.trials.insert_one(to_json(trial))
            return trial

        except DuplicateKeyError:
            trial.revision += 1
            info(f'Trial already exist increasing revision (rev: {trial.revision})')
            return self.new_trial(trial)
Exemple #6
0
    def __init__(self, uri):
        uri = parse_uri(uri)
        self.username = uri.get('username')
        self.password = uri.get('password')
        self.security_layer = uri['query'].get('security_layer')
        self.socket = open_socket(uri.get('address'),
                                  int(uri.get('port')),
                                  backend=self.security_layer)

        self.token = self._authenticate(uri)
        info(f'token: {self.token}')
Exemple #7
0
    def get_project(self, project: Project):
        kwargs = dict()
        kwargs['__rpc__'] = 'get_project'
        kwargs['project'] = to_json(project)

        info(kwargs)
        send(self.socket, kwargs)
        p = _check(recv(self.socket))

        info(f'got reply {p}')
        return p
Exemple #8
0
    def run_server(self):
        info(f'Server listening to {self.address}:{self.port}')
        self.sckt = listen_socket(self.address,
                                  self.port,
                                  backend=self.security_layer)

        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        loop.create_task(
            asyncio.start_server(self.handle_client, sock=self.sckt))
        loop.run_forever()

        self.loop = loop
Exemple #9
0
    def set_project(self,
                    project: Optional[Project] = None,
                    force: bool = False,
                    get_only: bool = False,
                    **kwargs):
        """Set or create a new project

        Parameters
        ----------
        project: Optional[Project]
            project definition you can use to create or set the project

        force: bool
            by default once the project is set it cannot be changed.
            use force to override this behaviour.

        get_only: bool
            if true does not insert the project if missing.
            default to false

        kwargs
            arguments used to create a `Project` object if no project object were provided
            See :func:`~track.structure.Project` for possible arguments

        Returns
        -------
        returns created project
        """
        if self.project is not None and not force:
            info('Project is already set, to override use force=True')
            return self.project

        if project is None:
            project = Project(**kwargs)

        assert project.name is not None, 'Project name cannot be none'

        # does the project exist ?
        self.project = self.protocol.get_project(project)

        if self.project is not None:
            return self.project

        if get_only:
            raise RuntimeError(
                f'Project (name: {project.name}) was not found!')

        self.project = self.protocol.new_project(project)

        debug(f'set project to (project: {self.project.name})')
        return self.project
Exemple #10
0
    def set_group(self,
                  group: Optional[TrialGroup] = None,
                  force: bool = False,
                  get_only: bool = False,
                  **kwargs):
        """Set or create a new group

        Parameters
        ----------
        group: Optional[TrialGroup]
            project definition you can use to create or set the project

        force: bool
            by default once the trial group is set it cannot be changed.
            use force to override this behaviour.

        get_only: bool
            if true does not insert the group if missing.
            default to false

        kwargs
            arguments used to create a `TrialGroup` object if no TrialGroup object were provided.
            See :func:`~track.structure.TrialGroup` for possible arguments

        Returns
        -------
        returns created trial group
        """

        if self.group is not None and not force:
            info('Group is already set, to override use force=True')
            return self.group

        if group is None:
            group = TrialGroup(**kwargs)

        if group.project_id is None:
            group.project_id = self.project.uid

        self.group = self.protocol.get_trial_group(group)

        if self.group is not None:
            return self.group

        if get_only:
            raise RuntimeError(f'Group (name: {group.name}) was not found!')

        self.group = self.protocol.new_trial_group(group)
        return self.group
Exemple #11
0
    def __init__(self, location, addrs, join=None, clean_on_exit=True):
        self.location = location

        logs = f'{location}/logs'
        temp = f'{location}/tmp'
        external = f'{location}/extern'
        store = location

        os.makedirs(logs, exist_ok=True)
        os.makedirs(temp, exist_ok=True)
        os.makedirs(external, exist_ok=True)

        self.location = location
        self.addrs = addrs
        self.bin = COCKROACH_BIN.get(os.name)

        if self.bin is None:
            raise RuntimeError('Your OS is not supported')

        if not os.path.exists(self.bin):
            info('Using system binary')
            self.bin = 'cockroach'
        else:
            hash = COCKROACH_HASH.get(os.name)
            if compute_version([self.bin]) != hash:
                warning('Binary Hashes do not match')

        self.arguments = [
            'start', '--insecure', f'--listen-addr={addrs}',
            f'--external-io-dir={external}', f'--store={store}',
            f'--temp-dir={temp}', f'--log-dir={logs}',
            f'--pid-file={location}/cockroach_pid'
        ]

        if join is not None:
            self.arguments.append(f'--join={join}')

        self.manager: Manager = Manager()
        self.properties = self.manager.dict()
        self.properties['running'] = False
        self.clean_on_exit = clean_on_exit
        self._process: Process = None
        self.cmd = None
Exemple #12
0
def _look_for_configuration(file_name='track.config'):
    config_file = None

    paths = {
        os.path.dirname(
            os.path.realpath(__file__)),  # location of the current file
        os.getcwd(),  # Current working directory
    }

    files = []
    for path in paths:
        file = f'{path}/{file_name}'

        if os.path.exists(file):
            files.append(file)
            config_file = file

    if len(files) > 1:
        warning(f'found multiple configuration file: {", ".join(files)}')
    elif config_file is not None:
        info(f'loading configuration from {config_file}')

    return config_file
Exemple #13
0
def start_track_server(protocol, hostname, port, security_layer=None):
    """Start a track server inside a asyncio loop

    Parameters
    ----------
    protocol: str
        URI that defines which backend to forward the request to

    hostname: str
        server host name

    port: int
        server port to listen to

    security_layer: str
        backend used for encryption (only AES is supported)
    """

    security = ''
    if security_layer is not None:
        security = f'&security_layer={security_layer}'

    server = SocketServer(f'socket://{hostname}:{port}?backend={protocol}' +
                          security)
    _ = ServerSignalHandler(server)

    try:
        info('Running Server')
        server.run_server()

    except KeyboardInterrupt as e:
        server.close()
        raise e

    except Exception as e:
        server.close()
        raise e
Exemple #14
0
    async def handle_client(self, reader, writer):
        info('Client Connected')

        running = True
        count = 0
        sleep_time = 0
        cache = {}
        info_proc = throttle_repeated(info, every=10)

        while running:
            request = await read(reader)
            count += 1

            if request is None:
                time.sleep(0.01)
                sleep_time += 0.01

                if sleep_time > self.timeout:
                    info(
                        f'Client (user: {self.get_username(reader)}) is timing out'
                    )
                    await self.close_connection(writer)
                    self.authentication.pop(reader, None)
                    return None
                continue

            proc_name = request.pop('__rpc__', None)
            info_proc(
                f'Processing Request: {proc_name} for (user: {self.get_username(reader)})'
            )

            if proc_name is None:
                error(f'Could not process message (rpc: {request})')
                write(
                    writer, {
                        'status': 1,
                        'error': f'Could not process message (rpc: {request})'
                    })
                continue

            elif proc_name == 'authenticate':
                request['reader'] = reader
                self.exec(reader,
                          writer,
                          proc_name,
                          self.authenticate,
                          request,
                          cache=cache)
                continue

            elif not self.is_authenticated(reader):
                error(
                    f'Client is not authenticated cannot execute (proc: {proc_name})'
                )
                write(
                    writer, {
                        'status':
                        1,
                        'error':
                        f'Client is not authenticated cannot execute (proc: {proc_name})'
                    })
                continue

            # Forward request to backend
            attr = getattr(self.backend, proc_name)

            if attr is None:
                error(
                    f'{self.backend.__name__} does not implement (rpc: {proc_name})'
                )
                write(
                    writer, {
                        'status':
                        1,
                        'error':
                        f'{self.backend.__name__} does not implement (rpc: {proc_name})'
                    })
                continue

            self.exec(reader, writer, proc_name, attr, request, cache=cache)

            sleep_time = 0

        self.authentication.pop(reader, None)