Source code for pybsd.systems.jails

# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals

import logging

from ..exceptions import AttachNonMasterError, DuplicateJailHostnameError, DuplicateJailNameError, DuplicateJailUidError, InvalidUIDError
from .base import BaseSystem

__logger__ = logging.getLogger('pybsd')


[docs]class Jail(BaseSystem): """Describes a jailed system When attached to an instance of :py:class:`~pybsd.systems.masters.Master` a jail can be created, deleted and controlled through said master's ezjail-admin interface. Example ------- >>> from pybsd import Jail, Master >>> master01 = Master(name='master01', ... hostname='master01.foo.bar', ... ext_if=('re0', ['148.241.178.106/24', '1c02:4f8:0f0:14e6::/110']), ... int_if=('eth0', ['192.168.0.0/24', '1c02:4f8:0f0:14e6::0:0:1/110']), ... j_if=('re0', ['10.0.1.0/24', '1c02:4f8:0f0:14e6::1:0:1/110']), ... jlo_if=('lo1', ['127.0.1.0/24', '::0:1:0:0/110'])) >>> jail01 = Jail(name='system', ... uid=12, ... hostname='system.foo.bar', ... master=None, ... auto_start=True, ... jail_class='web') Parameters ---------- name : :py:class:`str` a name that identifies the jail. uid : :py:class:`int` The jail's id, unique over a user's or an organization's domain. hostname : Optional[:py:class:`str`] The jail's hostname. It not specified the jail's name is used instead. #: Optional[:py:class:`~pybsd.systems.masters.Master`]: master : Optional[:py:class:`~pybsd.systems.masters.Master`] The jail's master i.e. host system. By default a :py:class:`~pybsd.systems.jails.Jail` is created detached and the value of master is None. jail_type : Optional[:py:class:`str`] The jail's type, according to its storage solution. If the jail is not attached it is set to None by default. If attached the default is `Z`, for ZFS filesystem-based jail. Possible types are: * **D** --> Directory tree based jail. * **I** --> File-based jail. * **E** --> Geli encrypted file-based jail. * **B** --> Bde encrypted file-based jail. * **Z** --> ZFS filesystem-based jail. auto_start : Optional[:py:class:`bool`] Whether the jail should be started automatically at host system's boot time. jail_class : Optional[:py:class:`str`] Allows differentiating jails by class. This will be worked out of base jails to depend on the jail handler. The base handler will probably not have the notion of classes Raises ------ AttachNonMasterError if `master` is specified and is not an instance of :py:class:`~pybsd.systems.masters.Master` DuplicateJailNameError if `master` is specified and the jail's name is already attached to it DuplicateJailHostnameError if `master` is specified and the jail's hostname is already attached to it DuplicateJailUidError if `master` is specified and the jail's uid is already attached to it JailAlreadyAttachedError if `master` is specified and the jail is already attached to another master """ def __init__(self, name, uid, hostname=None, master=None, auto_start=False, jail_class='service'): super(Jail, self).__init__(name=name, hostname=hostname) if uid: self._uid = uid else: raise InvalidUIDError(master, self) #: Optional[:py:class:`bool`]: Whether the jail should be started automatically at host system's boot time. self.auto_start = auto_start #: Optional[:py:class:`str`]: Allows differentiating jails by class. self.jail_class = jail_class #: Optional[:py:class:`~pybsd.systems.masters.Master`]: The jail's master i.e. host system. By default a #: :py:class:`~pybsd.systems.jails.Jail` is created detached. self.master = None if master: try: master.attach_jail(self) except AttributeError: raise AttachNonMasterError(master, self) @property def is_attached(self): """:py:class:`bool`: Whether the jail is currently attached to a master.""" return self.master is not None @property def handler(self): """:py:class:`bool`: Whether the jail is currently attached to a master.""" return self.master.jail_handler if self.is_attached else None @property def name(self): """:py:class:`str`: a name that identifies the system.""" return self._name @name.setter def name(self, name): if self.is_attached: if name in self.master.names: raise DuplicateJailNameError(self.master, self, name) self._name = name @property def base_hostname(self): """:py:class:`str` or :py:class:`NoneType`: The system's hostname.""" return self._hostname @property def hostname(self): """:py:class:`str` or :py:class:`NoneType`: The jail's hostname. If not attached, it. is equal to None""" if self.is_attached: return self._hostname or self.handler.get_jail_hostname(self) else: return None @hostname.setter def hostname(self, hostname): if self.is_attached: if hostname in self.master.hostnames: raise DuplicateJailHostnameError(self.master, self, hostname) self._hostname = hostname @property def uid(self): """:py:class:`int`: The jail's uid.""" return self._uid @uid.setter def uid(self, uid): if self.is_attached: if uid in self.master.uids: raise DuplicateJailUidError(self.master, self, uid) self._uid = uid @property def jail_type(self): """:py:class:`str` or :py:class:`NoneType`: The jail's type, according to its storage solution. If not attached, it. is equal to None Possible types are: * **D** --> Directory tree based jail. * **I** --> File-based jail. * **E** --> Geli encrypted file-based jail. * **B** --> Bde encrypted file-based jail. * **Z** --> ZFS filesystem-based jail. """ return self.handler.get_jail_type(self) if self.is_attached else None @property def status(self): """:py:class:`str`: Returns this jail's status as per ezjail_admin Possible status * **D** The jail is detached (not attached to any master) * **S** The jail is stopped. * **A** The image of the jail is mounted, but the jail is not running. * **R** The jail is running. The `S` value is a stub for now. It must come from parsing master's ezjail-admin.list()'s output """ if self.is_attached: return 'S' else: return 'D' @property def jid(self): """:py:class:`int`: Returns this jail's jid as per ezjail_admin The `1` value returned when attached is a stub for now. It must come from parsing master's ezjail-admin.list()'s output """ if self.is_attached: return 1 else: return None @property def jail_class_id(self): """:py:class:`int`: Returns this jail's class id. This is an integer value which is given by its jail_handler according to its class. """ if self.is_attached: return self.handler.jail_class_ids[self.jail_class] else: return None @property def path(self): """:py:class:`unipath.Path`: the absolute path of the jail's filesystem, relative to the host's filesystem. It is evaluated dynamically by the master's jail handler, so that the same base jail cloned on different host systems can return different values. By default it resolves to a directory called after jail.name, inside the host system's jail_path: foo.path = unipath.Path('/usr/jails/foo'). """ if self.is_attached: return self.handler.get_jail_path(self) else: return None @property def ext_if(self): """:py:class:`~pybsd.network.Interface`: the jail's outward-facing interface. It is evaluated dynamically by the master's jail handler, so that the same base jail cloned on different host systems can return different values. """ if self.is_attached: return self.handler.get_jail_ext_if(self) else: return None @property def lo_if(self): """:py:class:`~pybsd.network.Interface`: the jail's loopback interface. It is evaluated dynamically by the master's jail handler, so that the same base jail cloned on different host systems can return different values. """ if self.is_attached: return self.handler.get_jail_lo_if(self) else: return None