# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
import logging
import re
import six
import sortedcontainers
from ..exceptions import DuplicateIPError
from ..executors import Executor
from ..network import Interface
__logger__ = logging.getLogger('pybsd')
IF_PROPERTY = re.compile(r'^\w*_if$')
[docs]class BaseSystem(object):
"""Describes a base OS instance such as a computer, a virtualized system or a jail
It provides common functionality for a full system, a jail or a virtualized instance.
This allows interaction with both real and modelized instances.
Parameters
----------
name : :py:class:`str`
a name that identifies the system.
hostname : Optional[:py:class:`str`]
The system's hostname.
Attributes
----------
ExecutorClass : :py:class:`class`
the class of the system's executor. It must be or extend :py:class:`~pybsd.executors.Executor`
"""
ExecutorClass = Executor
def __init__(self, name, hostname=None):
super(BaseSystem, self).__init__()
self._name = name
self._hostname = hostname
#: :py:class:`~function`: a method that proxies binaries invocations
self.execute = self.ExecutorClass()
@property
def name(self):
""":py:class:`str`: a name that identifies the system."""
return self._name
@name.setter
def name(self, name):
self._name = name
@property
def hostname(self):
""":py:class:`str`: The system's hostname. If not specified, the system's name is returned instead."""
return self._hostname or self.name
@hostname.setter
def hostname(self, hostname):
self._hostname = hostname
def __repr__(self):
# Maps the system's string representation to its hostname
#
# Returns
# -------
# : :py:class:`str`
# the system's hostname
return self.name
[docs]class System(BaseSystem):
"""Describes a full OS instance
It provides common functionality for a full system.
**Interfaces**
Each interface is described by:
:py:class:`tuple` (interface_name (:py:class:`str`), :py:class:`list` [ip_interfaces (:py:class:`str`)]).
Each ip interface is composed of an ip and an optional prefixlen, such as::
('re0', ['10.0.2.0/24', '10.0.1.0/24', '1c02:4f8:0f0:14e6::2:0:1/110', '1c02:4f8:0f0:14e6::1:0:1/110'])
if the prefixlen is not specified it will default to /32 (IPv4) or /128 (IPv6)
Example
-------
>>> from pybsd import System
>>> box01 = System(name='box01',
... hostname='box01.foo.bar',
... ext_if=('re0', ['148.241.178.106/24', '1c02:4f8:0f0:14e6::/110', '1c02:4f8:000:14e6::/110']),
... int_if=('eth0', ['192.168.0.0/24', '1c02:4f8:0f0:14e6::0:0:1/110'])
... )
>>> '148.241.178.106' in box01.ips
True
>>> '148.241.178.101' in box01.ips
False
>>> box01.ips
SortedSet(['127.0.0.1', '148.241.178.106', '192.168.0.0', '1c02:4f8:0:14e6::', '1c02:4f8:f0:14e6::', '1c02:4f8:f0:14e6::1', \
'::1'], key=None, load=1000)
Parameters
----------
name : :py:class:`str`
a name that identifies the system.
ext_if : :py:class:`tuple` (:py:class:`str`, :py:class:`list` [:py:class:`str`])
Interface definition used to initialize self.ext_if
int_if : Optional[ :py:class:`tuple` (:py:class:`str`, :py:class:`list` [:py:class:`str`]) ]
Interface definition used to initialize self.int_if
lo_if : Optional[ :py:class:`tuple` (:py:class:`str`, :py:class:`list` [:py:class:`str`]) ]
Interface definition used to initialize self.lo_if
hostname : Optional[:py:class:`int`]
The system's hostname.
Raises
------
DuplicateIPError
if any ip address in the interface definitions is already in use.
"""
def __init__(self, name, ext_if, int_if=None, lo_if=None, hostname=None):
super(System, self).__init__(name=name, hostname=hostname)
#: :py:class:`~pybsd.network.Interface`: the system's outward-facing interface
self.ext_if = self.make_if(ext_if)
self._int_if = self.make_if(int_if)
lo_if = lo_if or ('lo0', ['127.0.0.1/8', '::1/110'])
#: :py:class:`~pybsd.network.Interface`: the system's loopback interface. If not expressly defined, it defaults to
#: ('lo0', ['127.0.0.1/8', '::1/110'])
self.lo_if = self.make_if(lo_if)
[docs] def make_if(self, definition):
"""Returns an :py:class:`~pybsd.network.Interface` based on `definition`
Parameters
----------
definition : :py:class:`tuple` (:py:class:`str`, :py:class:`list` [:py:class:`str`])
Returns
-------
: :py:class:`~pybsd.network.Interface`
a valid interface
Raises
------
DuplicateIPError
raised if one of the ip addresses in `definition` is already in use
"""
if not definition:
return None
if_name, if_ips = definition
_if = Interface(name=if_name, ips=if_ips)
intersec = _if.ips.intersection(self.ips)
if len(intersec):
raise DuplicateIPError(self, _if, intersec)
return _if
@property
def int_if(self):
""":py:class:`~pybsd.network.Interface`: the system's internal network-facing interface. If not expressly defined, it defaults to
self.ext_if, as in that case the same interface will be used for all networks."""
return self._int_if or self.ext_if
[docs] def reset_int_if(self):
"""Resets the system's int_if to its default value (its own ext_if)"""
self._int_if = None
@property
def ips(self):
""":py:class:`sortedcontainers.SortedSet` ([ :py:class:`str` ]): a sorted set containing all ips on this system."""
ips = sortedcontainers.SortedSet()
for prop, interface in six.iteritems(self.__dict__):
if IF_PROPERTY.match(prop) and interface:
ips.update([x.ip.compressed for x in interface.ifsv4 + interface.ifsv6])
return ips