Merge pull request #13 from EinDev/master

Add more API functions & Formatting
This commit is contained in:
Martin Raiber 2022-02-15 22:54:07 +01:00 committed by GitHub
commit 66e5bee13d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -10,354 +10,353 @@ import os
import binascii
import logging
from enum import Enum
logger = logging.getLogger('urbackup-server-python-api-wrapper')
class installer_os(Enum):
Windows = "windows",
Linux = "linux"
Windows = "windows",
Linux = "linux"
class urbackup_server:
def __init__(self, server_url, server_username, server_password):
self._server_url=server_url
self._server_username=server_username
self._server_password=server_password
#If you have basic authentication via .htpasswd
def __init__(self, server_url, server_username, server_password):
self._server_url = server_url
self._server_username = server_username
self._server_password = server_password
# If you have basic authentication via .htpasswd
server_basic_username = ''
server_basic_password = ''
_session=""
_session = ""
_logged_in = False
_lastlogid = 0
def _get_response(self, action, params, method="POST"):
headers = {
'Accept': 'application/json',
'Content-Type': 'application/json; charset=UTF-8'
}
if('server_basic_username' in globals() and len(self.server_basic_username)>0):
userAndPass = b64encode(str.encode(self.server_basic_username+":"+self.server_basic_password)).decode("ascii")
headers['Authorization'] = 'Basic %s' % userAndPass
curr_server_url=self._server_url+"?"+urlencode({"a": action});
if(len(self._session)>0):
params["ses"]=self._session
if method==None:
if ('server_basic_username' in globals() and len(self.server_basic_username) > 0):
userAndPass = b64encode(str.encode(self.server_basic_username + ":" + self.server_basic_password)).decode(
"ascii")
headers['Authorization'] = 'Basic %s' % userAndPass
curr_server_url = self._server_url + "?" + urlencode({"a": action});
if (len(self._session) > 0):
params["ses"] = self._session
if method == None:
method = 'POST'
if method=="GET":
curr_server_url+="&"+urlencode(params);
if method == "GET":
curr_server_url += "&" + urlencode(params);
target = urlparse(curr_server_url)
if method=='POST':
if method == 'POST':
body = urlencode(params)
else:
body = ''
http_timeout = 10*60;
if(target.scheme=='http'):
http_timeout = 10 * 60;
if (target.scheme == 'http'):
h = http.HTTPConnection(target.hostname, target.port, timeout=http_timeout)
elif(target.scheme=='https'):
elif (target.scheme == 'https'):
h = http.HTTPSConnection(target.hostname, target.port, timeout=http_timeout)
else:
logger.error('Unkown scheme: '+target.scheme)
raise Exception("Unkown scheme: "+target.scheme)
logger.error('Unkown scheme: ' + target.scheme)
raise Exception("Unkown scheme: " + target.scheme)
h.request(
method,
target.path+"?"+target.query,
body,
headers)
method,
target.path + "?" + target.query,
body,
headers)
return h.getresponse();
def _get_json(self, action, params = {}):
def _get_json(self, action, params={}):
tries = 50
while tries>0:
while tries > 0:
response = self._get_response(action, params)
if(response.status == 200):
if (response.status == 200):
break
tries=tries-1
if(tries==0):
tries = tries - 1
if (tries == 0):
return None
else:
logger.error("API call failed. Retrying...")
data = response.read();
response.close()
return json.loads(data.decode("utf-8","ignore"))
return json.loads(data.decode("utf-8", "ignore"))
def _download_file(self, action, outputfn, params):
response = self._get_response(action, params, "GET");
if(response.status!=200):
if (response.status != 200):
return False
with open(outputfn, 'wb') as outputf:
shutil.copyfileobj(response, outputf)
return True
return True
def _md5(self, s):
return hashlib.md5(s.encode()).hexdigest()
def login(self):
if( not self._logged_in):
if (not self._logged_in):
logger.debug("Trying anonymous login...")
login = self._get_json("login", {});
if(not login or 'success' not in login or not login['success']):
if (not login or 'success' not in login or not login['success']):
logger.debug("Logging in...")
salt = self._get_json("salt", {"username": self._server_username})
if( not salt or not ('ses' in salt) ):
if (not salt or not ('ses' in salt)):
logger.warning('Username does not exist')
return False
self._session = salt["ses"];
if( 'salt' in salt ):
password_md5_bin = hashlib.md5((salt["salt"]+self._server_password).encode()).digest()
if ('salt' in salt):
password_md5_bin = hashlib.md5((salt["salt"] + self._server_password).encode()).digest()
password_md5 = binascii.hexlify(password_md5_bin).decode()
if "pbkdf2_rounds" in salt:
pbkdf2_rounds = int(salt["pbkdf2_rounds"])
if pbkdf2_rounds>0:
password_md5 = binascii.hexlify(hashlib.pbkdf2_hmac('sha256', password_md5_bin,
salt["salt"].encode(), pbkdf2_rounds)).decode()
password_md5 = self._md5(salt["rnd"]+password_md5)
login = self._get_json("login", { "username": self._server_username,
"password": password_md5 })
if(not login or 'success' not in login or not login['success']):
if pbkdf2_rounds > 0:
password_md5 = binascii.hexlify(hashlib.pbkdf2_hmac('sha256', password_md5_bin,
salt["salt"].encode(),
pbkdf2_rounds)).decode()
password_md5 = self._md5(salt["rnd"] + password_md5)
login = self._get_json("login", {"username": self._server_username,
"password": password_md5})
if (not login or 'success' not in login or not login['success']):
logger.warning('Error during login. Password wrong?')
return False
else:
self._logged_in=True
self._logged_in = True
return True
else:
return False
else:
self._logged_in=True
self._logged_in = True
self._session = login["session"];
return True
return True
else:
return True
def get_client_status(self, clientname):
if not self.login():
return None
status = self._get_json("status")
if not status:
return None
if not "status" in status:
return None
for client in status["status"]:
if (client["name"] == clientname):
return client;
logger.warning("Could not find client status. No permission?")
return None
def download_installer(self, installer_fn, new_clientname, e_installer_os):
if not self.login():
return False
new_client = self._get_json("add_client", { "clientname": new_clientname })
new_client = self._get_json("add_client", {"clientname": new_clientname})
if "already_exists" in new_client:
status = self.get_client_status(new_clientname)
if status == None:
return False
return self._download_file("download_client", installer_fn,
{"clientid": status["id"],
"os": e_installer_os.value})
if not "new_authkey" in new_client:
{"clientid": status["id"],
"os": e_installer_os.value})
if not "new_authkey" in new_client:
return False
return self._download_file("download_client", installer_fn,
{"clientid": new_client["new_clientid"],
"authkey": new_client["new_authkey"],
"os": e_installer_os.value
})
{"clientid": new_client["new_clientid"],
"authkey": new_client["new_authkey"],
"os": e_installer_os.value
})
def add_client(self, clientname):
if not self.login():
return None
ret = self._get_json("add_client", { "clientname": clientname})
if ret==None or "already_exists" in ret:
ret = self._get_json("add_client", {"clientname": clientname})
if ret == None or "already_exists" in ret:
return None
return ret
def get_global_settings(self):
if not self.login():
return None
settings = self._get_json("settings", {"sa": "general"} )
settings = self._get_json("settings", {"sa": "general"})
if not settings or not "settings" in settings:
return None
return settings["settings"]
def set_global_setting(self, key, new_value):
if not self.login():
return False
settings = self._get_json("settings", {"sa": "general"} )
settings = self._get_json("settings", {"sa": "general"})
if not settings or not "settings" in settings:
return False
settings["settings"][key] = new_value
settings["settings"]["sa"] = "general_save"
ret = self._get_json("settings", settings["settings"])
return ret!=None and "saved_ok" in ret
return ret != None and "saved_ok" in ret
def get_client_settings(self, clientname):
if not self.login():
return None
client = self.get_client_status(clientname)
if client == None:
return None
clientid = client["id"];
settings = self._get_json("settings", {"sa": "clientsettings",
"t_clientid": clientid})
"t_clientid": clientid})
if not settings or not "settings" in settings:
return None
return settings["settings"]
def change_client_setting(self, clientname, key, new_value):
if not self.login():
return False
client = self.get_client_status(clientname)
if client == None:
return False
clientid = client["id"];
settings = self._get_json("settings", {"sa": "clientsettings",
"t_clientid": clientid})
"t_clientid": clientid})
if not settings or not "settings" in settings:
return False
settings["settings"][key] = new_value
settings["settings"]["overwrite"] = "true"
settings["settings"]["sa"] = "clientsettings_save"
settings["settings"]["t_clientid"] = clientid
ret = self._get_json("settings", settings["settings"])
return ret!=None and "saved_ok" in ret
return ret != None and "saved_ok" in ret
def get_client_authkey(self, clientname):
if not self.login():
return None
settings = self.get_client_settings(clientname)
if settings:
return settings["internet_authkey"]
return None
def get_server_identity(self):
if not self.login():
return None
status = self._get_json("status")
if not status:
return None
if not "server_identity" in status:
return None
return status["server_identity"]
def get_status(self):
if not self.login():
return None
status = self._get_json("status")
if not status:
return None
if not "status" in status:
return None
return status["status"]
def get_users(self):
if not self.login():
return None
users = self._get_json("settings",{"sa":"listusers"})
users = self._get_json("settings", {"sa": "listusers"})
if not "users" in users:
return None
return users["users"]
def get_livelog(self, clientid = 0):
def get_livelog(self, clientid=0):
if not self.login():
return None
@ -372,7 +371,7 @@ class urbackup_server:
self._lastlogid = log["logdata"][-1]['id']
return log["logdata"]
def get_usage(self):
if not self.login():
return None
@ -390,103 +389,131 @@ class urbackup_server:
def get_extra_clients(self):
if not self.login():
return None
status = self._get_json("status")
if not status:
return None
if not "extra_clients" in status:
return None
return status["extra_clients"]
def _start_backup(self, clientname, backup_type):
client_info = self.get_client_status(clientname)
if not client_info:
return False
ret = self._get_json("start_backup", {"start_client": client_info["id"],
"start_type": backup_type } );
if ( ret==None
or "result" not in ret
or len(ret["result"])!=1
or "start_ok" not in ret["result"][0]
or not ret["result"][0]["start_ok"] ):
ret = self._get_json("start_backup", {"start_client": client_info["id"],
"start_type": backup_type});
if (ret == None
or "result" not in ret
or len(ret["result"]) != 1
or "start_ok" not in ret["result"][0]
or not ret["result"][0]["start_ok"]):
return False
return True
def start_incr_file_backup(self, clientname):
return self._start_backup(clientname, 'incr_file');
def start_full_file_backup(self, clientname):
return self._start_backup(clientname, 'full_file');
def start_incr_image_backup(self, clientname):
return self._start_backup(clientname, 'incr_image');
def start_full_image_backup(self, clientname):
return self._start_backup(clientname, 'full_image');
def get_clientimagebackups(self, clientid = 0):
def get_clientimagebackups(self, clientid=0):
if not self.login():
return None
backups = self._get_json("backups", { "sa": "backups", "clientid": clientid })
backups = self._get_json("backups", {"sa": "backups", "clientid": clientid})
return backups["backup_images"]
def get_clientbackups(self, clientid = 0):
def get_clientbackups(self, clientid=0):
if not self.login():
return None
backups = self._get_json("backups", { "sa": "backups", "clientid": clientid })
backups = self._get_json("backups", {"sa": "backups", "clientid": clientid})
return backups["backups"]
def get_backup_content(self, clientid, backupid, path="/"):
if not self.login():
return None
content = self._get_json("backups", {
"sa": "files",
"clientid": clientid,
"backupid": backupid,
"path": path
})
return content["files"]
def download_backup_file(self, clientid, backupid, path="/"):
if not self.login():
return None
response = self._get_response("backups", {
"sa": "filesdl",
"clientid": clientid,
"backupid": backupid,
"path": path
}, "GET")
if response.status != 200:
return None
return response.read()
def get_groups(self):
if not self.login():
return None
settings = self._get_json("settings")
return settings["navitems"]["groups"]
def get_clients_with_group(self):
if not self.login():
return None
settings = self._get_json("settings")
return settings["navitems"]["clients"]
def add_extra_client(self, addr):
if not self.login():
return None
ret = self._get_json("status", {"hostname": addr } );
ret = self._get_json("status", {"hostname": addr})
if not ret:
return False
return True
def remove_extra_client(self, ecid):
if not self.login():
return None
ret = self._get_json("status", {"hostname": ecid,
"remove": "true" } );
"remove": "true"})
if not ret:
return False
return True
action_incr_file = 1
action_full_file = 2
action_incr_image = 3
@ -499,32 +526,31 @@ class urbackup_server:
action_check_db_integrity = 11
action_backup_db = 12
action_recalc_stats = 13
def get_actions(self):
if not self.login():
return None
ret = self._get_json("progress")
if not ret or not "progress" in ret:
return None
return ret["progress"]
def stop_action(self, action):
if (not "clientid" in action
or not "id" in action):
or not "id" in action):
return False
if not self.login():
return None
ret = self._get_json("progress",
{"stop_clientid": action["clientid"],
"stop_id": action["id"]})
if not ret or not "progress" in ret:
return False
return True