install
Base installation helpers
To import...
from dynamite_nsm.services.base import install
BaseInstallManager
An interface used to assist with a variety of common service installation tasks
__init__(self, name, verbose=False, requires_root=True, stdout=True, log_level=20)
special
Build a custom service installer
Parameters:
Name | Type | Description | Default |
---|---|---|---|
name |
str |
The name of the service |
required |
requires_root |
Optional[bool] |
If True, then the uninstaller will check that the user is root |
True |
stdout |
Optional[bool] |
Print output to console |
True |
verbose |
Optional[bool] |
Include detailed debug messages |
False |
log_level |
|
The minimum logging.LOG_LEVEL to be handled |
20 |
Source code in dynamite_nsm/services/base/install.py
def __init__(self, name: str, verbose: Optional[bool] = False, requires_root: Optional[bool] = True,
stdout: Optional[bool] = True, log_level=logging.INFO):
"""
Build a custom service installer
Args:
name: The name of the service
requires_root: If True, then the uninstaller will check that the user is root
stdout: Print output to console
verbose: Include detailed debug messages
log_level: The minimum logging.LOG_LEVEL to be handled
"""
if not utilities.is_setup():
raise exceptions.DynamiteNotSetupError()
if requires_root and not utilities.is_root():
raise exceptions.RequiresRootError()
if verbose:
log_level = logging.DEBUG
self.stdout = stdout
self.verbose = verbose
self.logger = get_logger(str(name).upper(), level=log_level, stdout=stdout)
self.dynamite_environ = utilities.get_environment_file_dict()
utilities.makedirs(const.INSTALL_CACHE)
utilities.create_dynamite_user()
compile_source_package(self, source_root_directory, compile_args=None, parallel_threads=None, expected_lines_printed=None)
A simple make; make install wrapper
Parameters:
Name | Type | Description | Default |
---|---|---|---|
source_root_directory |
str |
The directory containing the MAKEFILE |
required |
compile_args |
Optional[List[str]] |
make arguments |
None |
parallel_threads |
Optional[int] |
The number of parallel threads to use during compilation (jemalloc) |
None |
expected_lines_printed |
Optional[int] |
The number of lines produced by this process (used to generate a progressbar) |
None |
Returns: None
Source code in dynamite_nsm/services/base/install.py
def compile_source_package(self, source_root_directory: str, compile_args: Optional[List[str]] = None,
parallel_threads: Optional[int] = None,
expected_lines_printed: Optional[int] = None) -> None:
"""A simple make; make install wrapper
Args:
source_root_directory: The directory containing the MAKEFILE
compile_args: make arguments
parallel_threads: The number of parallel threads to use during compilation (jemalloc)
expected_lines_printed: The number of lines produced by this process (used to generate a progressbar)
Returns: None
"""
if not parallel_threads:
parallel_threads = get_parallel_threads()
if compile_args:
compile_args.extend(['-j', parallel_threads])
else:
compile_args = ['-j', parallel_threads]
temp_compile_args = [f'{const.SYS_BIN}/make']
temp_compile_args.extend(compile_args)
temp_compile_args = [str(a) for a in temp_compile_args]
compile_args = temp_compile_args
compile_args.extend([';', f'{const.SYS_BIN}/make', 'install'])
self.logger.info(f'Compiling: {source_root_directory}.')
self.logger.debug(" ".join(compile_args))
popen_make_args = dict(
args=' '.join(compile_args),
shell=True,
cwd=source_root_directory,
)
if not self.verbose:
popen_make_args['stdout'] = subprocess.PIPE
popen_make_args['stderr'] = subprocess.PIPE
ret = utilities.run_subprocess_with_status(subprocess.Popen(**popen_make_args),
expected_lines=expected_lines_printed)
else:
p = subprocess.Popen(**popen_make_args)
p.communicate()
ret = p.returncode
if ret != 0:
self.logger.error(f'Exited: {ret}; Process Info: {compile_args}')
raise exceptions.CallProcessError(f'Exited with {ret}')
configure_source_package(self, source_root_directory, configure_args=None)
A configure wrapper for the build/make process
Parameters:
Name | Type | Description | Default |
---|---|---|---|
source_root_directory |
str |
A directory containing the configuration.in file |
required |
configure_args |
Optional[List[str]] |
configure arguments |
None |
Returns:
Type | Description |
---|---|
None |
None |
Source code in dynamite_nsm/services/base/install.py
def configure_source_package(self, source_root_directory: str, configure_args: Optional[List[str]] = None) -> None:
"""A configure wrapper for the build/make process
Args:
source_root_directory: A directory containing the configuration.in file
configure_args: configure arguments
Returns:
None
"""
temp_config_args = ['./configure']
temp_config_args.extend(configure_args)
temp_config_args = [str(a) for a in temp_config_args]
configure_args = temp_config_args
self.logger.info(f'Configuring build: {source_root_directory}.')
self.logger.debug(" ".join(configure_args))
popen_args = dict(
args=' '.join(configure_args),
shell=True,
cwd=source_root_directory,
)
if not self.verbose:
popen_args['stdout'] = subprocess.PIPE
popen_args['stderr'] = subprocess.PIPE
ret = utilities.run_subprocess_with_status(subprocess.Popen(**popen_args),
expected_lines=None)
else:
p = subprocess.Popen(**popen_args)
p.communicate()
ret = p.returncode
if ret != 0:
self.logger.error(f'Exited: {ret}; Process Info: {configure_args}')
raise exceptions.CallProcessError(f'Exited with {ret}')
copy_file_or_directory_to_destination(self, file_or_dir, destination_file_or_dir)
Copy a file or directory to another file or directory
Parameters:
Name | Type | Description | Default |
---|---|---|---|
file_or_dir |
str |
The file or directory to copy |
required |
destination_file_or_dir |
str |
The file or directory destination |
required |
Returns:
Type | Description |
---|---|
None |
None |
Source code in dynamite_nsm/services/base/install.py
def copy_file_or_directory_to_destination(self, file_or_dir: str, destination_file_or_dir: str) -> None:
"""
Copy a file or directory to another file or directory
Args:
file_or_dir: The file or directory to copy
destination_file_or_dir: The file or directory destination
Returns:
None
"""
file_or_dir = file_or_dir.rstrip('/')
destination_location = f'{destination_file_or_dir}/{os.path.basename(file_or_dir)}'
if os.path.isdir(file_or_dir):
utilities.makedirs(destination_location, exist_ok=True)
self.logger.debug(f'Creating directory: {destination_location}')
try:
self.logger.debug(f'Copying directory {file_or_dir} -> {destination_location}')
utilities.copytree(file_or_dir, destination_location)
except shutil.Error as e:
if 'exist' in str(e):
self.logger.warning(f'{destination_file_or_dir} directory already exists. Skipping.')
else:
raise e
else:
try:
self.logger.debug(f'Copying file {file_or_dir} -> {destination_file_or_dir}')
shutil.copy(file_or_dir, destination_file_or_dir)
except FileNotFoundError:
parent_directory = os.path.dirname(destination_file_or_dir)
self.logger.debug(f'Creating parent directory: {parent_directory}')
utilities.makedirs(parent_directory)
shutil.copy(file_or_dir, destination_file_or_dir)
except shutil.Error as e:
if 'exist' in str(e):
self.logger.warning(f'{destination_file_or_dir} file already exists. Skipping.')
else:
raise e
create_update_env_variable(self, name, value)
Write the environment variable into our root owned environment file
Parameters:
Name | Type | Description | Default |
---|---|---|---|
name |
str |
The name of the variable |
required |
value |
str |
The value of the variable |
required |
Returns:
Type | Description |
---|---|
None |
None |
Source code in dynamite_nsm/services/base/install.py
def create_update_env_variable(self, name: str, value: str) -> None:
"""Write the environment variable into our root owned environment file
Args:
name: The name of the variable
value: The value of the variable
Returns:
None
"""
name = str(name)
value = str(value)
env_file_path = f'{const.CONFIG_PATH}/environment'
if not os.path.exists(env_file_path):
with open(env_file_path, 'w') as env_f:
env_f.write('')
overwrite_line_no = -1
with open(env_file_path) as env_fr:
read_lines = env_fr.readlines()
for idx, line in enumerate(read_lines):
if str(line).startswith(name):
overwrite_line_no = idx
break
if overwrite_line_no == -1:
with open(env_file_path, 'a') as env_fa:
env_fa.write(f'{name}={value}\n')
self.logger.debug(f'Setting {name} -> {value}')
else:
self.logger.debug(f'Overwriting {name} -> {value}')
if value.endswith('\n'):
read_lines[overwrite_line_no] = f'{name}={value}'
else:
read_lines[overwrite_line_no] = f'{name}={value}\n'
with open(env_file_path, 'w') as env_fw:
env_fw.writelines(read_lines)
utilities.set_ownership_of_file(env_file_path)
download_from_mirror(self, mirror_path)
Download a Dynamite service from a mirror
Parameters:
Name | Type | Description | Default |
---|---|---|---|
mirror_path |
str |
The path to the mirror |
required |
Returns:
Type | Description |
---|---|
Tuple[str, str, Optional[str]] |
The mirror url, archive name, and directory name (once the archive has been extracted) |
Source code in dynamite_nsm/services/base/install.py
def download_from_mirror(self, mirror_path: str) -> Tuple[str, str, Optional[str]]:
"""Download a Dynamite service from a mirror
Args:
mirror_path: The path to the mirror
Returns:
The mirror url, archive name, and directory name (once the archive has been extracted)
"""
with open(mirror_path) as mirror_f:
res, err = None, None
for mirror in mirror_f.readlines():
try:
url, archive_name, dir_name = [token.strip() for token in mirror.split(',')]
except ValueError:
url = mirror
archive_name = os.path.basename(url)
dir_name = None
self.logger.info("Downloading {} from {}".format(archive_name, url))
fqdn_dir_name = f'{const.INSTALL_CACHE}/{str(dir_name)}'
if os.path.exists(fqdn_dir_name):
shutil.rmtree(fqdn_dir_name, ignore_errors=True)
try:
res = utilities.download_file(url, archive_name, stdout=self.stdout)
except Exception as e:
res, err = False, e
self.logger.warning(f'Failed to download {archive_name} from {url}; {e}')
if res:
break
if not res:
self.logger.error(f'An error occurred while attempting to download: {err}')
raise exceptions.DownloadError(
f'General error while attempting to download {archive_name} from all mirrors.')
return url, archive_name, dir_name
extract_archive(archive_path)
staticmethod
Extract a tar.gz archive to disk
Parameters:
Name | Type | Description | Default |
---|---|---|---|
archive_path |
str |
The full path to the archive. |
required |
Returns:
Type | Description |
---|---|
None |
None |
Source code in dynamite_nsm/services/base/install.py
@staticmethod
def extract_archive(archive_path: str) -> None:
"""
Extract a tar.gz archive to disk
Args:
archive_path: The full path to the archive.
Returns:
None
"""
try:
tf = tarfile.open(archive_path)
tf.extractall(path=const.INSTALL_CACHE)
except IOError as e:
raise exceptions.ArchiveExtractionError(
f'Could not extract {archive_path} archive to {const.INSTALL_CACHE}; {e}')
except Exception as e:
raise exceptions.ArchiveExtractionError(
f'General error while attempting to extract {archive_path} archive; {e}')
get_mirror_info(mirror_path)
staticmethod
Get information about a mirror
Parameters:
Name | Type | Description | Default |
---|---|---|---|
mirror_path |
str |
The path to the mirror |
required |
Returns:
Type | Description |
---|---|
Tuple[str, str, Optional[str]] |
The mirror url, archive name, and directory name (once the archive has been extracted) |
Source code in dynamite_nsm/services/base/install.py
@staticmethod
def get_mirror_info(mirror_path: str) -> Tuple[str, str, Optional[str]]:
"""
Get information about a mirror
Args:
mirror_path: The path to the mirror
Returns:
The mirror url, archive name, and directory name (once the archive has been extracted)
"""
with open(mirror_path) as mirror_f:
for mirror in mirror_f.readlines():
try:
url, archive_name, dir_name = [token.strip() for token in mirror.split(',')]
except ValueError:
url = mirror
archive_name = os.path.basename(url)
dir_name = None
return url, archive_name, dir_name
install_dependencies(self, apt_get_packages=None, yum_packages=None, pre_install_function=None)
Install OS dependencies through the package manager
Parameters:
Name | Type | Description | Default |
---|---|---|---|
apt_get_packages |
Optional[List] |
The name of the packages available in apt-get supported repos |
None |
yum_packages |
Optional[List] |
The name of the packages available in yum supported repos |
None |
pre_install_function |
Optional[Callable] |
A Python function to run prior to installing these packages |
None |
Returns:
Type | Description |
---|---|
None |
None |
Source code in dynamite_nsm/services/base/install.py
def install_dependencies(self, apt_get_packages: Optional[List] = None, yum_packages: Optional[List] = None,
pre_install_function: Optional[Callable] = None) -> None:
"""
Install OS dependencies through the package manager
Args:
apt_get_packages: The name of the packages available in apt-get supported repos
yum_packages: The name of the packages available in yum supported repos
pre_install_function: A Python function to run prior to installing these packages
Returns:
None
"""
pacman = package_manager.OSPackageManager(stdout=self.stdout, verbose=self.verbose)
packages = []
if pacman.package_manager == 'apt-get':
self.logger.info('apt-get detected. We will use this package manager to install dependencies.')
packages = apt_get_packages
elif pacman.package_manager == 'yum':
self.logger.info('yum detected. We will use this package manager to install dependencies.')
packages = yum_packages
self.logger.info('Refreshing package indexes')
if pre_install_function:
self.logger.info('Running pre-installation function.')
pre_install_function(pacman.package_manager)
pacman.refresh_package_indexes()
self.logger.debug(f'Packages: {packages}')
if packages:
self.logger.info(f'Installing {len(packages)} new packages.')
pacman.install_packages(packages)
validate_inspect_interfaces(inspect_interfaces)
staticmethod
Determine if one or more capture interface actually exists
Parameters:
Name | Type | Description | Default |
---|---|---|---|
inspect_interfaces |
List[str] |
A list of network interface names to evaluate. |
required |
Returns:
Type | Description |
---|---|
bool |
True, if all interfaces are valid |
Source code in dynamite_nsm/services/base/install.py
@staticmethod
def validate_inspect_interfaces(inspect_interfaces: List[str]) -> bool:
"""
Determine if one or more capture interface actually exists
Args:
inspect_interfaces: A list of network interface names to evaluate.
Returns:
True, if all interfaces are valid
"""
for interface in inspect_interfaces:
if interface not in utilities.get_network_interface_names():
return False
return True
BaseUninstallManager
An interface used to assist with a variety of common service uninstall tasks
__init__(self, name, directories, environ_vars=None, process=None, sysctl_service_name=None, requires_root=True, verbose=False, stdout=True, log_level=20)
special
Remove installed files for a given service
Parameters:
Name | Type | Description | Default |
---|---|---|---|
name |
str |
The name of the process |
required |
directories |
List[str] |
The directories to be removed |
required |
process |
Optional[dynamite_nsm.services.base.process.BaseProcessManager] |
The process to be terminated |
None |
sysctl_service_name |
Optional[str] |
The name any associated systemd unit file. |
None |
requires_root |
Optional[bool] |
If True, then the uninstaller will check that the user is root |
True |
stdout |
Optional[bool] |
Print output to console |
True |
verbose |
Optional[bool] |
Include detailed debug messages |
False |
log_level |
|
The logging.LOG_LEVEL to use when logging |
20 |
Source code in dynamite_nsm/services/base/install.py
def __init__(self, name: str, directories: List[str], environ_vars: Optional[List[str]] = None,
process: Optional[process.BaseProcessManager] = None,
sysctl_service_name: Optional[str] = None, requires_root: Optional[bool] = True,
verbose: Optional[bool] = False,
stdout: Optional[bool] = True, log_level=logging.INFO):
"""Remove installed files for a given service
Args:
name: The name of the process
directories: The directories to be removed
process: The process to be terminated
sysctl_service_name: The name any associated systemd unit file.
requires_root: If True, then the uninstaller will check that the user is root
stdout: Print output to console
verbose: Include detailed debug messages
log_level: The logging.LOG_LEVEL to use when logging
"""
self.name = name
self.directories = directories
self.environ_vars = environ_vars
self.process = process
self.verbose = verbose
self.sysctl_service_name = sysctl_service_name
self.stdout = stdout
if verbose:
log_level = logging.DEBUG
self.logger = get_logger(str(name).upper(), level=log_level, stdout=stdout)
if requires_root and not utilities.is_root():
raise exceptions.RequiresRootError()
uninstall(self)
Stop and uninstall the service
Returns:
Type | Description |
---|---|
|
None |
Source code in dynamite_nsm/services/base/install.py
def uninstall(self):
"""Stop and uninstall the service
Returns:
None
"""
sysctl = systemctl.SystemCtl(stdout=self.stdout, verbose=self.verbose)
if self.process:
self.process.stop()
for dir in self.directories:
self.logger.info(f'Removing {dir}')
shutil.rmtree(dir, ignore_errors=True)
if self.environ_vars:
for var in self.environ_vars:
self.delete_env_variable(var)
if self.sysctl_service_name:
try:
self.logger.info(f'Uninstalling {self.sysctl_service_name}')
sysctl.uninstall_and_disable(self.sysctl_service_name)
except FileNotFoundError:
self.logger.debug('Skipping service uninstallation as systemd was not implemented in this setup.')
self.logger.info(f'Successfully uninstalled {self.name}')
NetworkInterfaceNotFound
Thrown when attempting to disable a non-existing interface
__init__(self, interfaces)
special
:param interfaces: A network interface
Source code in dynamite_nsm/services/base/install.py
def __init__(self, interfaces: Union[str, List]):
"""
:param interfaces: A network interface
"""
msg = f'Network interface(s) does not exist: {interfaces}.'
super(NetworkInterfaceNotFound, self).__init__(msg)