Coverage for volexport/main.py: 93%
75 statements
« prev ^ index » next coverage.py v7.10.4, created at 2025-08-20 14:19 +0000
« prev ^ index » next coverage.py v7.10.4, created at 2025-08-20 14:19 +0000
1import os
2import click
3import uvicorn
4import functools
5from typing import Optional
6from logging import getLogger
7from .version import VERSION
9_log = getLogger(__name__)
12@click.version_option(version=VERSION, prog_name="volexport")
13@click.group(invoke_without_command=True)
14@click.pass_context
15def cli(ctx):
16 if ctx.invoked_subcommand is None: 16 ↛ 17line 16 didn't jump to line 17 because the condition on line 16 was never true
17 click.echo(ctx.get_help())
20def set_verbose(verbose: Optional[bool]):
21 from logging import basicConfig
23 fmt = "%(asctime)s %(levelname)s %(name)s %(message)s"
24 level = "INFO"
25 if verbose:
26 level = "DEBUG"
27 elif verbose is not None: 27 ↛ 29line 27 didn't jump to line 29 because the condition on line 27 was always true
28 level = "WARNING"
29 basicConfig(level=level, format=fmt)
32def verbose_option(func):
33 @functools.wraps(func)
34 def wrap(verbose, *args, **kwargs):
35 set_verbose(verbose)
36 return func(*args, **kwargs)
38 return click.option("--verbose/--quiet", help="log level")(wrap)
41@cli.command()
42@verbose_option
43@click.option("--become-method", help="sudo/doas/runas, etc...")
44@click.option("--tgtadm-bin", help="tgtadm command")
45@click.option("--tgt-bstype", help="backing store type")
46@click.option("--tgt-bsopts", help="bs options")
47@click.option("--tgt-bsoflags", help="bs open flags")
48@click.option("--lvm-bin", help="lvm command")
49@click.option("--nics", multiple=True, help="use interfaces")
50@click.option("--iqn-base", help="iSCSI target basename")
51@click.option("--vg", help="LVM volume group")
52@click.option("--host", default="127.0.0.1", help="listen host")
53@click.option("--port", default=8080, type=int, help="listen port")
54@click.option("--log-config", type=click.Path(), help="uvicorn log config")
55@click.option("--cmd-timeout", type=float, help="command execution timeout")
56def server(host, port, log_config, **kwargs):
57 import json
59 for k, v in kwargs.items():
60 if k is None or v is None or (isinstance(v, tuple) and len(v) == 0):
61 continue
62 kk = f"VOLEXP_{k.upper()}"
63 if isinstance(v, tuple):
64 vv = json.dumps(list(v))
65 else:
66 vv = v
67 os.environ[kk] = vv
69 from .api import api
70 from .config import config
72 _log.debug("config: %s", config)
73 if log_config is None: 73 ↛ 76line 73 didn't jump to line 76 because the condition on line 73 was always true
74 getLogger("uvicorn").setLevel("INFO")
76 uvicorn.run(api, host=host, port=port, log_config=log_config)
79@cli.command()
80@click.option("--format", type=click.Choice(["yaml", "json"]), default="yaml", show_default=True)
81def apispec(format):
82 import sys
84 os.environ["VOLEXP_VG"] = "dummy"
85 os.environ["VOLEXP_NICS"] = "[]"
86 from .api import api
88 if format == "yaml":
89 import yaml
91 yaml.dump(api.openapi(), stream=sys.stdout)
92 elif format == "json": 92 ↛ exitline 92 didn't return from function 'apispec' because the condition on line 92 was always true
93 import json
95 json.dump(api.openapi(), fp=sys.stdout)
98if __name__ == "__main__": 98 ↛ 99line 98 didn't jump to line 99 because the condition on line 98 was never true
99 cli()