Skip to content

Commit

Permalink
Merge fe0aada into 8c9b3d6
Browse files Browse the repository at this point in the history
  • Loading branch information
frankcorneliusmartin committed Jan 11, 2023
2 parents 8c9b3d6 + fe0aada commit a9d52f5
Show file tree
Hide file tree
Showing 47 changed files with 1,410 additions and 408 deletions.
2 changes: 1 addition & 1 deletion docs/use/client.rst
Expand Up @@ -207,7 +207,7 @@ set up and if they have access to the private key.

.. code:: python
from vantage6.common import (warning, error, info, debug, bytes_to_base64s, check_config_write_permissions)
from vantage6.common import warning, error, info, debug, bytes_to_base64s
from vantage6.client.encryption import RSACryptor
from pathlib import Path
Expand Down
4 changes: 2 additions & 2 deletions requirements.txt
Expand Up @@ -7,7 +7,7 @@ bcrypt==3.1.7
bidict==0.21.2
blinker==1.4
cached-property==1.5.2
certifi==2020.4.5.1
certifi==2022.12.7
cffi==1.14.6
chardet==3.0.4
charset-normalizer==2.0.4
Expand Down Expand Up @@ -78,7 +78,7 @@ SQLAlchemy==1.3.15
termcolor==1.1.0
traitlets==5.0.5
typing-extensions==3.10.0.0
urllib3==1.25.11
urllib3==1.26.5
uWSGI==2.0.20
vine==5.0.0
wcwidth==0.2.5
Expand Down
4 changes: 1 addition & 3 deletions vantage6-client/requirements.txt
@@ -1,8 +1,6 @@
cryptography==3.3.2
requests==2.25.1
PyJWT==2.4.0
pyfiglet==0.8.post1
SPARQLWrapper==1.8.5
pandas==1.3.5
qrcode==7.3.1

qrcode==7.3.1
1 change: 0 additions & 1 deletion vantage6-client/setup.py
Expand Up @@ -34,7 +34,6 @@
packages=find_namespace_packages(),
python_requires='>=3.6',
install_requires=[
'cryptography==3.3.2',
'requests==2.25.1',
'PyJWT==2.4.0',
'pandas==1.3.5',
Expand Down
129 changes: 87 additions & 42 deletions vantage6-client/vantage6/client/__init__.py
Expand Up @@ -18,14 +18,15 @@

from pathlib import Path
from typing import Dict, Tuple, Union
from vantage6.client.utils import print_qr_code

from vantage6.common.exceptions import AuthenticationException
from vantage6.common import bytes_to_base64s, base64s_to_bytes
from vantage6.common.globals import APPNAME
from vantage6.common.encryption import RSACryptor, DummyCryptor
from vantage6.common import WhoAmI
from vantage6.client import serialization, deserialization
from vantage6.client.filter import post_filtering
from vantage6.client.encryption import RSACryptor, DummyCryptor
from vantage6.client.utils import print_qr_code, LogLevel


module_name = __name__.split('.')[1]
Expand All @@ -40,23 +41,6 @@ class ServerInfo(typing.NamedTuple):
path: str


class WhoAmI(typing.NamedTuple):
""" Data-class to store Authenticable information in."""
type_: str
id_: int
name: str
organization_name: str
organization_id: int

def __repr__(self) -> str:
return (f"<WhoAmI "
f"name={self.name}, "
f"type={self.type_}, "
f"organization={self.organization_name}, "
f"(id={self.organization_id})"
">")


class ClientBase(object):
"""Common interface to the central server.
Expand Down Expand Up @@ -335,8 +319,8 @@ def authenticate(self, credentials: dict,
print_qr_code(data)
return False
else:
# If no QR two-factor authentication is
# required, but that is not an error
# Check if there is an access token. If not, there is a problem
# with authenticating
if 'access_token' not in data:
if 'msg' in data:
raise Exception(data['msg'])
Expand Down Expand Up @@ -562,7 +546,7 @@ def __init__(self, parent):
class UserClient(ClientBase):
"""User interface to the vantage6-server"""

def __init__(self, *args, verbose=False, **kwargs):
def __init__(self, *args, verbose=False, log_level='debug', **kwargs):
"""Create user client
All paramters from `ClientBase` can be used here.
Expand All @@ -575,7 +559,8 @@ def __init__(self, *args, verbose=False, **kwargs):
super(UserClient, self).__init__(*args, **kwargs)

# Replace logger by print logger
self.log = self.Log(verbose)
# TODO in v4+, remove the verbose option and only keep log_level
self.log = self.get_logger(verbose, log_level)

# attach sub-clients
self.util = self.Util(self)
Expand All @@ -602,24 +587,42 @@ def __init__(self, *args, verbose=False, **kwargs):
self.log.info("https://vantage6.ai/vantage6/references")
self.log.info("-" * 60)

class Log:
"""Replaces the default logging meganism by print statements"""
def __init__(self, enabled: bool):
"""Create print-logger
@staticmethod
def get_logger(enabled: bool, level: str) -> logging.Logger:
"""
Create print-logger
Parameters
----------
enabled : bool
Whenever to enable logging
"""
self.enabled = enabled
for level in ['debug', 'info', 'warn', 'warning', 'error',
'critical']:
self.__setattr__(level, self.print)
Parameters
----------
enabled: bool
If true, logging at most detailed level
level: str
Desired logging level
def print(self, msg: str) -> None:
if self.enabled:
print(f'{msg}')
Returns
-------
logging.Logger
Logger object
"""
# get logger that prints to console
logger = logging.getLogger()
logger.handlers.clear()
logger.addHandler(logging.StreamHandler(sys.stdout))

# set log level
level = level.upper()
if enabled:
logger.setLevel(LogLevel.DEBUG.value)
elif level not in [lvl.value for lvl in LogLevel]:
default_lvl = LogLevel.DEBUG.value
logger.setLevel(default_lvl)
logger.warn(
f"You set unknown log level {level}. Available levels are: "
f"{', '.join([lvl.value for lvl in LogLevel])}. ")
logger.warn(f"Log level now set to {default_lvl}.")
else:
logger.setLevel(level)
return logger

def authenticate(self, username: str, password: str,
mfa_code: Union[int, str] = None) -> None:
Expand Down Expand Up @@ -1171,6 +1174,24 @@ def delete(self, id_: int) -> dict:
"""
return self.parent.request(f'node/{id_}', method='delete')

def kill_tasks(self, id_: int) -> dict:
"""
Kill all tasks currently running on a node
Parameters
----------
id_ : int
Id of the node of which you want to kill the tasks
Returns
-------
dict
Message from the server
"""
return self.parent.request(
'kill/node/tasks', method='post', json={'id': id_}
)

class Organization(ClientBase.SubClient):
"""Collection of organization requests"""

Expand Down Expand Up @@ -1666,8 +1687,8 @@ def list(self, initiator: int = None, initiating_user: int = None,
parent: int = None, run: int = None,
name: str = None, include_results: bool = False,
description: str = None, database: str = None,
result: int = None, page: int = 1, per_page: int = 20,
include_metadata: bool = True) -> dict:
result: int = None, status: str = None, page: int = 1,
per_page: int = 20, include_metadata: bool = True) -> dict:
"""List tasks
Parameters
Expand Down Expand Up @@ -1697,6 +1718,9 @@ def list(self, initiator: int = None, initiating_user: int = None,
Filter by database (with LIKE operator)
result: int, optional
Only show task that contains this result id
status: str, optional
Filter by task status (e.g. 'active', 'pending', 'completed',
'crashed')
page: int, optional
Pagination page, by default 1
per_page: int, optional
Expand Down Expand Up @@ -1731,7 +1755,7 @@ def list(self, initiator: int = None, initiating_user: int = None,
'image': image, 'parent_id': parent, 'run_id': run,
'name': name, 'page': page, 'per_page': per_page,
'description': description, 'database': database,
'result_id': result
'result_id': result, 'status': status,
}
includes = []
if include_results:
Expand Down Expand Up @@ -1796,6 +1820,27 @@ def delete(self, id_: int) -> dict:
msg = self.parent.request(f'task/{id_}', method='delete')
self.parent.log.info(f'--> {msg}')

def kill(self, id_: int) -> dict:
"""Kill a task running on one or more nodes
Note that this does not remove the task from the database, but
merely halts its execution (and prevents it from being restarted).
Parameters
----------
id_ : int
Id of the task to be killed
Returns
-------
dict
Message from the server
"""
msg = self.parent.request('/kill/task', method='post', json={
'id': id_
})
self.parent.log.info(f'--> {msg}')

class Result(ClientBase.SubClient):

@post_filtering(iterable=False)
Expand Down
9 changes: 9 additions & 0 deletions vantage6-client/vantage6/client/utils.py
@@ -1,5 +1,6 @@
import qrcode

from enum import Enum
from typing import Dict


Expand Down Expand Up @@ -43,3 +44,11 @@ def show_qr_code_image(qr_uri: str) -> None:
qr.add_data(qr_uri)
qr.make(fit=True)
qr.print_ascii()


class LogLevel(Enum):
DEBUG = 'DEBUG'
INFO = 'INFO'
WARN = 'WARN'
ERROR = 'ERROR'
CRITICAL = 'CRITICAL'

0 comments on commit a9d52f5

Please sign in to comment.