Add startup command
This commit is contained in:
parent
dd9d610178
commit
645c758e50
32
README.md
32
README.md
|
@ -26,21 +26,12 @@ Create a new dataset called `jailmaker` with the default settings (from TrueNAS
|
|||
cd /mnt/mypool/jailmaker
|
||||
curl --location --remote-name https://raw.githubusercontent.com/Jip-Hop/jailmaker/main/jlmkr.py
|
||||
chmod +x jlmkr.py
|
||||
```
|
||||
|
||||
The `jlmkr.py` script (and the jails + config it creates) are now stored on the `jailmaker` dataset and will survive updates of TrueNAS SCALE.
|
||||
|
||||
### Install Jailmaker Dependencies
|
||||
|
||||
Unfortunately since version 22.12.3 TrueNAS SCALE no longer includes systemd-nspawn. In order to use jailmaker, we need to first install systemd-nspawn using the command below.
|
||||
|
||||
```shell
|
||||
./jlmkr.py install
|
||||
```
|
||||
|
||||
We need to do this again after each update of TrueNAS SCALE. So it is recommended to schedule this command as Post Init Script (see [Autostart Jail on Boot](#autostart-jail-on-boot)).
|
||||
The `jlmkr.py` script (and the jails + config it creates) are now stored on the `jailmaker` dataset and will survive updates of TrueNAS SCALE. Additionally a symlink has been created so you can call `jlmkr` from anywhere.
|
||||
|
||||
Additionally the install command will create a symlink from `/usr/local/sbin/jlmkr` to `jlmkr.py`. Thanks this this you can now run the `jlmkr` command from anywhere (instead of having to run `./jlmkr.py` from inside the directory where you've placed it).
|
||||
After an update of TrueNAS SCALE the symlink will be lost and `systemd-nspawn` (the core package which makes `jailmaker` work) may be gone too. Not to worry, just run `./jlmkr.py install` again or use [the `./jlmkr.py startup` command](#startup-jails-on-boot).
|
||||
|
||||
## Usage
|
||||
|
||||
|
@ -54,9 +45,18 @@ jlmkr create myjail
|
|||
|
||||
After answering a few questions you should have your first jail up and running!
|
||||
|
||||
#### Autostart Jail on Boot
|
||||
### Startup Jails on Boot
|
||||
|
||||
In order to start a jail automatically after TrueNAS boots, run `jlmkr start myjail` as Post Init Script with Type `Command` from the TrueNAS web interface. If you want to automatically install systemd-nspawn if it's not already installed (recommended to keep working after a TrueNAS SCALE update) then you may use a command such as this instead: `/mnt/mypool/jailmaker/jlmkr.py install && jlmkr start myjail`.
|
||||
```shell
|
||||
# Best to call startup directly (not through the jlmkr symlink)
|
||||
/mnt/mypool/jailmaker/jlmkr.py startup
|
||||
|
||||
# Can be called from the symlink too...
|
||||
# But this may not be available after a TrueNAS SCALE update
|
||||
jlmkr startup
|
||||
```
|
||||
|
||||
In order to start jails automatically after TrueNAS boots, run `/mnt/mypool/jailmaker/jlmkr.py startup` as Post Init Script with Type `Command` from the TrueNAS web interface. This will automatically fix the installation of `systemd-nspawn` and setup the `jlmkr` symlink, as well as start all the jails with `startup=1` in the config file. Running the `startup` command Post Init is recommended to keep `jailmaker` working after a TrueNAS SCALE update.
|
||||
|
||||
### Start Jail
|
||||
|
||||
|
@ -134,11 +134,11 @@ See [Advanced Networking](./NETWORKING.md) for more.
|
|||
|
||||
## Docker
|
||||
|
||||
Jailmaker won't install Docker for you, but it can setup the jail with the capabilities required to run docker. You can manually install Docker inside the jail using the [official installation guide](https://docs.docker.com/engine/install/#server) or use [convenience script](https://get.docker.com).
|
||||
The `jailmaker` script won't install Docker for you, but it can setup the jail with the capabilities required to run docker. You can manually install Docker inside the jail using the [official installation guide](https://docs.docker.com/engine/install/#server) or use [convenience script](https://get.docker.com).
|
||||
|
||||
## Nvidia GPU
|
||||
|
||||
To make passthrough of the nvidia GPU work, you need to schedule a Pre Init command. The reason is that TrueNAS SCALE by default doesn't load the nvidia kernel modules (and jailmaker doesn't do that either). [This screenshot](https://user-images.githubusercontent.com/1704047/222915803-d6dd51b0-c4dd-4189-84be-a04d38cca0b3.png) shows what the configuration should look like.
|
||||
To make passthrough of the nvidia GPU work, you need to schedule a Pre Init command. The reason is that TrueNAS SCALE by default doesn't load the nvidia kernel modules (and `jailmaker` doesn't do that either). [This screenshot](https://user-images.githubusercontent.com/1704047/222915803-d6dd51b0-c4dd-4189-84be-a04d38cca0b3.png) shows what the configuration should look like.
|
||||
|
||||
```
|
||||
[ ! -f /dev/nvidia-uvm ] && modprobe nvidia-current-uvm && /usr/bin/nvidia-modprobe -c0 -u
|
||||
|
@ -146,7 +146,7 @@ To make passthrough of the nvidia GPU work, you need to schedule a Pre Init comm
|
|||
|
||||
## Comparison
|
||||
|
||||
TODO: write comparison between systemd-nspawn (without jailmaker), LXC, VMs, Docker (on the host).
|
||||
TODO: write comparison between systemd-nspawn (without `jailmaker`), LXC, VMs, Docker (on the host).
|
||||
|
||||
### Incompatible Distros
|
||||
|
||||
|
|
132
jlmkr.py
132
jlmkr.py
|
@ -210,28 +210,48 @@ def stop_jail(jail_name):
|
|||
subprocess.run(["machinectl", "poweroff", jail_name])
|
||||
|
||||
|
||||
def start_jail(jail_name):
|
||||
"""
|
||||
Start jail with given name.
|
||||
"""
|
||||
|
||||
if jail_is_running(jail_name):
|
||||
fail(
|
||||
f"Skipped starting jail {jail_name}. It appears to be running already...")
|
||||
|
||||
jail_path = get_jail_path(jail_name)
|
||||
jail_config_path = get_jail_config_path(jail_name)
|
||||
|
||||
def parse_config(jail_config_path):
|
||||
config = configparser.ConfigParser()
|
||||
try:
|
||||
# Workaround to read config file without section headers
|
||||
config.read_string('[DEFAULT]\n'+Path(jail_config_path).read_text())
|
||||
except FileNotFoundError:
|
||||
fail(f'Unable to find: {jail_config_path}.')
|
||||
eprint(f'Unable to find config file: {jail_config_path}.')
|
||||
return
|
||||
|
||||
config = dict(config['DEFAULT'])
|
||||
|
||||
print("Config loaded!")
|
||||
return config
|
||||
|
||||
|
||||
def start_jail(jail_name, check_startup_enabled=False):
|
||||
"""
|
||||
Start jail with given name.
|
||||
"""
|
||||
skip_start_message = f"Skipped starting jail {jail_name}. It appears to be running already..."
|
||||
|
||||
if not check_startup_enabled and jail_is_running(jail_name):
|
||||
fail(skip_start_message)
|
||||
|
||||
jail_path = get_jail_path(jail_name)
|
||||
jail_config_path = get_jail_config_path(jail_name)
|
||||
|
||||
config = parse_config(jail_config_path)
|
||||
|
||||
if not config:
|
||||
fail(f'Aborting...')
|
||||
|
||||
# Only start if the startup setting is enabled in the config
|
||||
if check_startup_enabled:
|
||||
if config.get('startup') == '1':
|
||||
# We should start this jail based on the startup config...
|
||||
if jail_is_running(jail_name):
|
||||
# ...but we can skip if it's already running
|
||||
eprint(skip_start_message)
|
||||
return
|
||||
else:
|
||||
# Skip starting this jail since the startup config setting isnot enabled
|
||||
return
|
||||
|
||||
systemd_run_additional_args = [
|
||||
f"--unit={SYMLINK_NAME}-{jail_name}",
|
||||
|
@ -327,27 +347,20 @@ def start_jail(jail_name):
|
|||
]
|
||||
|
||||
print(dedent(f"""
|
||||
Starting jail with the following command:
|
||||
Starting jail {jail_name} with the following command:
|
||||
|
||||
{shlex.join(cmd)}
|
||||
|
||||
Starting jail with name: {jail_name}
|
||||
"""))
|
||||
|
||||
try:
|
||||
subprocess.run(cmd, check=True)
|
||||
except subprocess.CalledProcessError:
|
||||
fail(dedent(f"""
|
||||
Failed to start the jail...
|
||||
Failed to start jail {jail_name}...
|
||||
In case of a config error, you may fix it with:
|
||||
{SYMLINK_NAME} edit {jail_name}
|
||||
"""))
|
||||
|
||||
print(dedent(f"""
|
||||
Get a shell:
|
||||
{COMMAND_NAME} shell {jail_name}
|
||||
"""))
|
||||
|
||||
|
||||
def cleanup(jail_path):
|
||||
"""
|
||||
|
@ -651,8 +664,17 @@ def create_jail(jail_name, distro='debian', release='bullseye'):
|
|||
systemd_nspawn_user_args = input("Additional flags: ") or ""
|
||||
# Disable tab auto completion
|
||||
readline.parse_and_bind('tab: self-insert')
|
||||
print()
|
||||
|
||||
print(dedent(f"""
|
||||
The `{COMMAND_NAME} startup` command can automatically ensure {COMMAND_NAME} is installed properly and start a selection of jails.
|
||||
This comes in handy when you want to automatically start multiple jails after booting TrueNAS SCALE (e.g. from a Post Init Script).
|
||||
"""))
|
||||
|
||||
startup = int(agree(
|
||||
f"Do you want to start this jail when running: {COMMAND_NAME} startup?", 'n'))
|
||||
|
||||
print()
|
||||
|
||||
jail_config_path = get_jail_config_path(jail_name)
|
||||
jail_rootfs_path = get_jail_rootfs_path(jail_name)
|
||||
|
||||
|
@ -786,6 +808,7 @@ def create_jail(jail_name, distro='debian', release='bullseye'):
|
|||
]
|
||||
|
||||
config = cleandoc(f"""
|
||||
startup={startup}
|
||||
docker_compatible={docker_compatible}
|
||||
gpu_passthrough_intel={gpu_passthrough_intel}
|
||||
gpu_passthrough_nvidia={gpu_passthrough_nvidia}
|
||||
|
@ -805,7 +828,7 @@ def create_jail(jail_name, distro='debian', release='bullseye'):
|
|||
raise error
|
||||
|
||||
print()
|
||||
if agree("Do you want to start the jail?", 'y'):
|
||||
if agree(f"Do you want to start jail {jail_name} right now?", 'y'):
|
||||
start_jail(jail_name)
|
||||
|
||||
|
||||
|
@ -890,6 +913,15 @@ def run_command_and_parse_json(command):
|
|||
return None
|
||||
|
||||
|
||||
def get_all_jail_names():
|
||||
try:
|
||||
jail_names = os.listdir(JAILS_DIR_PATH)
|
||||
except FileNotFoundError:
|
||||
jail_names = []
|
||||
|
||||
return jail_names
|
||||
|
||||
|
||||
def list_jails():
|
||||
"""
|
||||
List all available and running jails.
|
||||
|
@ -898,16 +930,13 @@ def list_jails():
|
|||
jails = {}
|
||||
empty_value_indicator = '-'
|
||||
|
||||
try:
|
||||
jail_dirs = os.listdir(JAILS_DIR_PATH)
|
||||
except FileNotFoundError:
|
||||
jail_dirs = []
|
||||
jail_names = get_all_jail_names()
|
||||
|
||||
if not jail_dirs:
|
||||
if not jail_names:
|
||||
print('No jails.')
|
||||
return
|
||||
|
||||
for jail in jail_dirs:
|
||||
for jail in jail_names:
|
||||
jails[jail] = {"name": jail, "running": False}
|
||||
|
||||
# Get running jails from machinectl
|
||||
|
@ -937,7 +966,19 @@ def list_jails():
|
|||
|
||||
# TODO: add additional properties from the jails config file
|
||||
|
||||
print_table(["name", "running", "os", "version", "addresses"],
|
||||
for jail_name in jails:
|
||||
|
||||
config = parse_config(get_jail_config_path(jail_name))
|
||||
|
||||
startup = False
|
||||
if config:
|
||||
startup = bool(int(config.get('startup', '0')))
|
||||
# TODO: in case config is missing or parsing fails,
|
||||
# should an error message be thrown here?
|
||||
|
||||
jails[jail_name]['startup'] = startup
|
||||
|
||||
print_table(["name", "running", "startup", "os", "version", "addresses"],
|
||||
sorted(jails.values(), key=lambda x: x['name']), empty_value_indicator)
|
||||
|
||||
|
||||
|
@ -983,6 +1024,12 @@ def install_jailmaker():
|
|||
print("Done installing jailmaker.")
|
||||
|
||||
|
||||
def startup_jails():
|
||||
install_jailmaker()
|
||||
for jail_name in get_all_jail_names():
|
||||
start_jail(jail_name, True)
|
||||
|
||||
|
||||
def main():
|
||||
if os.stat(SCRIPT_PATH).st_uid != 0:
|
||||
fail(
|
||||
|
@ -1042,6 +1089,9 @@ def main():
|
|||
subparsers.add_parser(name='images', epilog=DISCLAIMER,
|
||||
help='list available images to create jails from')
|
||||
|
||||
subparsers.add_parser(name='startup', epilog=DISCLAIMER,
|
||||
help=f'install {SYMLINK_NAME} and startup selected jails')
|
||||
|
||||
if os.getuid() != 0:
|
||||
parser.print_usage()
|
||||
fail("Run this script as root...")
|
||||
|
@ -1054,7 +1104,13 @@ def main():
|
|||
|
||||
args, additional_args = parser.parse_known_args()
|
||||
|
||||
if args.subcommand == 'start':
|
||||
if args.subcommand == 'install':
|
||||
install_jailmaker()
|
||||
|
||||
elif args.subcommand == 'create':
|
||||
create_jail(args.name)
|
||||
|
||||
elif args.subcommand == 'start':
|
||||
start_jail(args.name)
|
||||
|
||||
elif args.subcommand == 'shell':
|
||||
|
@ -1072,9 +1128,6 @@ def main():
|
|||
elif args.subcommand == 'stop':
|
||||
stop_jail(args.name)
|
||||
|
||||
elif args.subcommand == 'create':
|
||||
create_jail(args.name)
|
||||
|
||||
elif args.subcommand == 'edit':
|
||||
edit_jail(args.name)
|
||||
|
||||
|
@ -1084,14 +1137,11 @@ def main():
|
|||
elif args.subcommand == 'list':
|
||||
list_jails()
|
||||
|
||||
elif args.subcommand == 'install':
|
||||
install_jailmaker()
|
||||
|
||||
elif args.subcommand == 'images':
|
||||
run_lxc_download_script()
|
||||
|
||||
elif args.subcommand:
|
||||
parser.print_usage()
|
||||
elif args.subcommand == 'startup':
|
||||
startup_jails()
|
||||
|
||||
else:
|
||||
if agree("Create a new jail?", 'y'):
|
||||
|
|
Loading…
Reference in New Issue