base command line functions
This commit is contained in:
parent
4f14fb406a
commit
1ff5ddf63f
108
backupdb2/cli.py
108
backupdb2/cli.py
|
@ -1,56 +1,113 @@
|
|||
import logging
|
||||
import argparse
|
||||
import logging
|
||||
import requests
|
||||
from backupdb2.misc import load_cli_config, tabulate
|
||||
|
||||
|
||||
class BackupdbClient(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
def __init__(self, base_url, passwd=None):
|
||||
self.session = requests.Session()
|
||||
if passwd:
|
||||
self.session.auth = passwd # user, pass tuple
|
||||
self.base_url = base_url
|
||||
if not self.base_url.endswith("/"):
|
||||
self.base_url += "/"
|
||||
|
||||
def get(self, url, **params):
|
||||
return self.do("get", url, **params)
|
||||
|
||||
def post(self, url, **params):
|
||||
return self.do("post", url, **params)
|
||||
|
||||
def delete(self, url, **params):
|
||||
return self.do("delete", url, **params)
|
||||
|
||||
def do(self, method, url, **kwargs):
|
||||
resp = getattr(self.session, method)(self.base_url + "api/v1/" + url, **kwargs)
|
||||
resp.raise_for_status()
|
||||
return resp
|
||||
|
||||
def list_namespaces(self):
|
||||
return self.get("namespaces").json()
|
||||
|
||||
def list_backups(self, namespace="default"):
|
||||
return self.get("backups", params=dict(namespace=namespace)).json()
|
||||
|
||||
def list_dates(self, backup, namespace="default"):
|
||||
return self.get("dates", params=dict(namespace=namespace, backup=backup)).json()
|
||||
|
||||
# def create_user(self, username, password):
|
||||
# return self.post("user", data={"username": username,
|
||||
# "password_hash": pwhash(password)})
|
||||
|
||||
# def upload(self, files, metadata):
|
||||
# return self.post("upload", files=files, data={"meta": json.dumps(metadata)})
|
||||
|
||||
|
||||
def cmd_list_configured(args, parser):
|
||||
def cmd_list_configured(args, parser, config, client):
|
||||
"""
|
||||
If there is a config file, list backups configured in it
|
||||
"""
|
||||
pass
|
||||
if not config["backups"]:
|
||||
print("No backups configured")
|
||||
return
|
||||
|
||||
tabulate([
|
||||
[
|
||||
name,
|
||||
c["dir"],
|
||||
c["method"],
|
||||
] for name, c in config["backups"].items()],
|
||||
headers=["name", "path", "method"]
|
||||
)
|
||||
|
||||
|
||||
def cmd_list_namespace(args, parser):
|
||||
def cmd_list_namespace(args, parser, config, client):
|
||||
"""
|
||||
List namespaces
|
||||
"""
|
||||
pass
|
||||
tabulate([
|
||||
[i] for i in client.list_namespaces()],
|
||||
headers=["namespaces"]
|
||||
)
|
||||
|
||||
|
||||
def cmd_list_backup(args, parser):
|
||||
def cmd_list_backup(args, parser, config, client):
|
||||
"""
|
||||
List backups in a namespace
|
||||
"""
|
||||
pass
|
||||
tabulate([
|
||||
[i] for i in client.list_backups(namespace=args.namespace)],
|
||||
headers=["backups"]
|
||||
)
|
||||
|
||||
|
||||
def cmd_list_dates(args, parser):
|
||||
def cmd_list_dates(args, parser, config, client):
|
||||
"""
|
||||
List available dates for a backup
|
||||
"""
|
||||
pass
|
||||
tabulate([
|
||||
[i] for i in client.list_dates(backup=args.backup, namespace=args.namespace)],
|
||||
headers=[args.backup]
|
||||
)
|
||||
|
||||
|
||||
def cmd_backup(args, parser):
|
||||
"""
|
||||
Create a new backup - requires it be defined in the config file
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def cmd_download(args, parser):
|
||||
def cmd_download(args, parser, config, client):
|
||||
"""
|
||||
Download a backup
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def cmd_restore(args, parser):
|
||||
def cmd_backup(args, parser, config, client):
|
||||
"""
|
||||
Create a new backup - requires it be defined in the config file
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def cmd_restore(args, parser, config, client):
|
||||
"""
|
||||
Restore a backup
|
||||
"""
|
||||
|
@ -62,10 +119,10 @@ def get_args():
|
|||
parser.add_argument("-c", "--config", help="config file path")
|
||||
parser.add_argument("-s", "--server", help="server address")
|
||||
|
||||
sp_action = parser.add_subparsers(dest="action", help="action to take")
|
||||
sp_action = parser.add_subparsers(dest="action", required=True, help="action to take")
|
||||
|
||||
p_list = sp_action.add_parser("list", help="list action")
|
||||
sp_list = p_list.add_subparsers(dest="list_action", help="item to list", required=True)
|
||||
sp_list = p_list.add_subparsers(dest="list_action", required=True, help="item to list")
|
||||
|
||||
p_list_configured = sp_list.add_parser("configured", help="list locally configured backups")
|
||||
p_list_configured.set_defaults(func=cmd_list_configured)
|
||||
|
@ -77,7 +134,7 @@ def get_args():
|
|||
p_list_backup.set_defaults(func=cmd_list_backup)
|
||||
p_list_backup.add_argument("-n", "--namespace", default="default", help="parent namespace to list in")
|
||||
|
||||
p_list_dates = sp_list.add_parser("date", help="list dates")
|
||||
p_list_dates = sp_list.add_parser("dates", help="list dates")
|
||||
p_list_dates.set_defaults(func=cmd_list_dates)
|
||||
p_list_dates.add_argument("-n", "--namespace", default="default", help="parent namespace to list in")
|
||||
p_list_dates.add_argument("backup", help="backup to list dates for")
|
||||
|
@ -106,8 +163,7 @@ def main():
|
|||
logging.basicConfig(level=logging.INFO)
|
||||
args, parser = get_args()
|
||||
|
||||
#TODO one of `config` or `server` or a default config file is required
|
||||
config = load_cli_config(args.config)
|
||||
client = BackupdbClient(args.server or config["options"].get("server"))
|
||||
|
||||
client = BackupdbClient()
|
||||
|
||||
args.func(args, parser)
|
||||
args.func(args, parser, config, client)
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
import os
|
||||
import logging
|
||||
from configparser import ConfigParser
|
||||
|
||||
|
||||
def load_cli_config(config_path=None):
|
||||
"""
|
||||
Load the CLI config file. The config is delivered as a dict structured like:
|
||||
{
|
||||
"options": {...},
|
||||
"backups": {
|
||||
"name": {
|
||||
"uri": null,
|
||||
"dir": null,
|
||||
"keep": null,
|
||||
"auth": null,
|
||||
"restore_preexec": null,
|
||||
"restore_postexec": null,
|
||||
"export_preexec": null,
|
||||
"export_postexec": null,
|
||||
"exclude": null
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
config = {
|
||||
"options": {},
|
||||
"backups": {}
|
||||
}
|
||||
|
||||
# local the config file
|
||||
if not config_path:
|
||||
config_format, config_path = locate_cli_config()
|
||||
else:
|
||||
config_format = get_config_format(config_path)
|
||||
|
||||
if not config_path or not os.path.exists(config_path):
|
||||
raise Exception(f"config file not found: {config_path}")
|
||||
|
||||
# TODO other formats and loaders
|
||||
if config_format == "ini":
|
||||
load_cli_config_ini(config, config_path)
|
||||
|
||||
# logging.info("no config file available")
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def locate_cli_config():
|
||||
if os.path.exists("/etc/datadb.ini"):
|
||||
return "ini", "/etc/datadb.ini"
|
||||
return None, None
|
||||
|
||||
|
||||
def get_config_format(fpath):
|
||||
return "ini"#TODO
|
||||
|
||||
|
||||
def load_cli_config_ini(config, config_path):
|
||||
"""
|
||||
The ini config file format is structured like:
|
||||
|
||||
[_backupdb_] ; global section
|
||||
server=https://foo.bar/ ; server http address
|
||||
username=foo ; http basic auth used for server (optional)
|
||||
password=bar ; http basic auth used for server (optional)
|
||||
namespace=default ; backup namespace
|
||||
|
||||
[backup_name] ; name of one backup config, must be unique
|
||||
method=archive ; backup method, only 'archive' is supported now
|
||||
dir=/foo ; dir to backup
|
||||
keep=6 ; how many copies to instruct the server to keep
|
||||
exclude= ; exclude pattern (passed directly to underlying implementation)
|
||||
restore_preexec= ; commands to run before/after restoring/backing up
|
||||
restore_postexec= ;
|
||||
export_preexec= ;
|
||||
export_postexec= ;
|
||||
"""
|
||||
|
||||
# defaults per backup section
|
||||
config_defaults = {
|
||||
"server": None,
|
||||
"username": None,
|
||||
"password": None,
|
||||
"namespace": "default"
|
||||
}
|
||||
backup_defaults = {
|
||||
"method": "archive",
|
||||
"dir": None,
|
||||
"keep": 7,
|
||||
"exclude": None,
|
||||
"restore_preexec": None,
|
||||
"restore_postexec": None,
|
||||
"export_preexec": None,
|
||||
"export_postexec": None
|
||||
}
|
||||
backup_required_params = set(["dir"])
|
||||
global_required_params = set(["server"])
|
||||
int_keys = set(["keep"])
|
||||
|
||||
# Load profiles
|
||||
parser = ConfigParser(inline_comment_prefixes=("#", ";", ))
|
||||
parser.read(config_path)
|
||||
|
||||
for section_name in parser.sections():
|
||||
section = parser[section_name]
|
||||
for name, options in section.items():
|
||||
if section_name == "_backupdb_": # parse main config
|
||||
for k, v in config_defaults.items():
|
||||
config["options"][k] = section.get(k) or v
|
||||
|
||||
validate_section(section_name, config["options"], global_required_params)
|
||||
|
||||
else: # parse a backup section
|
||||
config["backups"][section_name] = {}
|
||||
for k, v in backup_defaults.items():
|
||||
value = section.get(k) or v
|
||||
if k in int_keys:
|
||||
value = int(value)
|
||||
config["backups"][section_name][k] = value
|
||||
|
||||
validate_section(section_name, config["backups"][section_name], backup_required_params)
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def validate_section(section_name, d, required_keys):
|
||||
for k in required_keys:
|
||||
if not d.get(k):
|
||||
raise Exception(f"config section '{section_name}' missing key '{k}'")
|
||||
|
||||
|
||||
def tabulate(rows, headers):
|
||||
lengths = [0] * len(headers)
|
||||
for row in rows + [headers]:
|
||||
for i, c in enumerate(row):
|
||||
row[i] = str(c)
|
||||
lengths[i] = max(lengths[i], len(row[i]))
|
||||
|
||||
for i, h in enumerate(headers):
|
||||
print(h + " " * (lengths[i] - len(h)) + " ", end="")
|
||||
print()
|
||||
for i, h in enumerate(headers):
|
||||
print("-" * lengths[i] + " ", end="")
|
||||
print()
|
||||
for row in rows:
|
||||
for i, c in enumerate(row):
|
||||
print(c + " " * (lengths[i] - len(c)) + " ", end="")
|
||||
print()
|
||||
print()
|
Loading…
Reference in New Issue