Luis Gerardo Romero Garcia 2024-09-12 09:00:08 +02:00
commit 981d0405f0
8 changed files with 2254 additions and 0 deletions

99
gitlib/README.md 100644
View File

@ -0,0 +1,99 @@
# GitLib
La `gitlib.py` es una librería de Python también usable como programa de línea
de comandos para pruebas.
Contiene las funciones de gestión de git, y la parte de línea de comandos permite ejecutarlas sin necesitar escribir un programa que use la librería.
# Instalación de dependencias para python
La conversion del código a Python 3 requiere actualmente los paquetes especificados en `requirements.txt`
Para instalar dependencias de python se usa el modulo venv (https://docs.python.org/3/library/venv.html) que instala todas las dependencias en un entorno independiente del sistema.
# Uso
## Distribuciones antiguas (18.04)
sudo apt install -y python3.8 python3.8-venv python3-venv libarchive-dev
python3.8 -m venv venvog
. venvog/bin/activate
python3.8 -m pip install --upgrade pip
pip3 install -r requirements.txt
Ejecutar con:
./gitlib.py
En modo de linea de comando, hay ayuda que se puede ver con:
./gitlib.py --help
Los comandos que comienzan por `--test` existen para hacer pruebas internas, y existen temporalmente para probar partes especificas del código. Es posible que necesiten condiciones especificas para funcionar, y van a eliminarse al completarse el desarrollo.
## Uso
**Nota:** Preferiblemente ejecutar como `root`, ya que `sudo` borra los cambios a las variables de entorno realizadas por venv. El resultado probable es un error de falta de módulos de Python, o un fallo del programa por usar dependencias demasiado antiguas.
# . venv/bin/activate
# ./opengnsys_git_installer.py
### Inicializar un repositorio:
./gitlib.py --init-repo /mnt/sda2/ --repo linux
Esto inicializa el repositorio 'linux' con el contenido /mnt/sda2.
`--repo` especifica el nombre de uno de los repositorios fijados durante la instalación de git (ver git installer).
El repositorio de sube al ogrepository, que se obtiene del parámetro de arranque pasado al kernel.
### Clonar un repositorio:
./gitlib.py --clone-repo-to /dev/sda2 --boot-device /dev/sda --repo linux
Esto clona un repositorio del ogrepository. El destino es un dispositivo físico que se va a formatear con el sistema de archivos necesario.
`--boot-device` especifica el dispositivo de arranque donde se va a instalar el bootloader (GRUB o similar)
`--repo` es el nombre de repositorio contenido en ogrepository.
# Documentación
Se puede generar documentación de Python con una utilidad como pdoc3 (hay multiples alternativas posibles):
# Instalar pdoc3
pip install --user pdoc3
# Generar documentación
pdoc3 --force --html opengnsys_git_installer.py
# Funcionamiento
## Requisitos
La gitlib esta diseñada para funcionar dentro de un entorno opengnsys existente. Invoca algunos de los comandos de opengnsys internamente, y lee los parámetros pasados al kernel en el oglive.
## Metadatos
Git no es capaz de almacenar datos de atributos extendidos, sockets y otros tipos de archivos especiales. El gitlib los almacena en .opengnsys-metadata en
el raíz del repositorio.
Los datos se guardan en archivos de tipo `jsonl`, una estructura de JSON por linea. Esto es para facilitar aplicaciones parciales solo aplicando el efecto de las lineas necesarias.
Existen estos archivos:
* `acls.jsonl`: ACLs
* `empty_directories.jsonl`: Directorios vacíos, ya que Git no es capaz de guardarlos
* `filesystems.json`: Información sobre sistemas de archivos: tipos, tamaños, UUIDs
* `gitignores.jsonl`: Lista de archivos .gitignore (los renombramos para que no interfieran con git)
* `metadata.json`: Metadatos generales acerca del repositorio
* `special_files.jsonl`: Archivos especiales como sockets
* `xattrs.jsonl`: Atributos extendidos

View File

@ -0,0 +1,52 @@
#!/usr/bin/env python3
import unittest
import logging
import os
import sys
import urllib.request
import tarfile
import subprocess
from shutil import rmtree
from pathlib import Path
parent_dir = str(Path(__file__).parent.parent.absolute())
sys.path.append(parent_dir)
sys.path.append("/opengnsys/installer")
print(parent_dir)
from gitlib import OpengnsysGitLibrary
class GitTests(unittest.TestCase):
def setUp(self):
self.logger = logging.getLogger("OpengnsysTest")
self.oggit = OpengnsysGitLibrary()
self.logger.info("setUp()")
if not hasattr(self, 'init_complete'):
self.init_complete = True
def test_init(self):
self.assertIsNotNone(self.oggit)
def test_acls(self):
self.oggit.ogCreateAcl()
def test_sync_local(self):
# self.oggit.ogSyncLocalGitImage()
None
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)20s - [%(levelname)5s] - %(message)s')
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logger.info("Inicio del programa")
unittest.main()

1439
gitlib/gitlib.py 100755

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,9 @@
gitdb==4.0.11
GitPython==3.1.43
libarchive==0.4.7
libarchive-c==5.1
nose==1.3.7
pylibacl==0.7.0
pylibblkid==0.3
pyxattr==0.8.1
smmap==5.0.1

View File

@ -0,0 +1,63 @@
# Instalación de dependencias para python
La conversion del código a Python 3 requiere actualmente los paquetes especificados en `requirements.txt`
Para instalar dependencias de python se usa el modulo venv (https://docs.python.org/3/library/venv.html) que instala todas las dependencias en un entorno independiente del sistema.
# Instalación rápida
## Distribuciones antiguas (18.04)
**Nota:** En 18.04, `uname` solo se encuentra en `/bin`, lo que causa un error inocuo en el log durante la creación de los repositorios:
Failed checking if running in CYGWIN due to: FileNotFoundError(2, 'No such file or directory')
Se arregla con el symlink incluido en las instrucciones mas abajo.
sudo apt install -y python3.8 python3.8-venv python3-venv libarchive-dev
sudo ln -sf /bin/uname /usr/bin/
python3.8 -m venv venvog
. venvog/bin/activate
python3.8 -m pip install --upgrade pip
pip3 install -r requirements.txt
Ejecutar con:
python3.8 ./opengnsys_git_installer.py
## Distribuciones nuevas (22.04)
sudo apt install python3 python3-venv libarchive-dev
python3 -m venv venvog
. venvog/bin/activate
python3 -m pip install --upgrade pip
pip3 install -r requirements.txt
## Agregar clave de SSH si es necesario
El proceso falla si no hay clave de SSH en la imagen. Utilizar:
/opt/opengnsys/bin/setsslkey
para agregarla.
# Ejecutar
**Nota:** Preferiblemente ejecutar como `root`, ya que `sudo` borra los cambios a las variables de entorno realizadas por venv. El resultado probable es un error de falta de módulos de Python, o un fallo del programa por usar dependencias demasiado antiguas.
# . venv/bin/activate
# ./opengnsys_git_installer.py
# Documentación
Se puede generar documentación de Python con una utilidad como pdoc3 (hay multiples alternativas posibles):
# Instalar pdoc3
pip install --user pdoc3
# Generar documentación
pdoc3 --force --html opengnsys_git_installer.py

View File

@ -0,0 +1,216 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Python: module opengnsys_git_installer</title>
</head><body>
<table class="heading">
<tr class="heading-text decor">
<td class="title">&nbsp;<br><strong class="title">opengnsys_git_installer</strong></td>
<td class="extra"><a href=".">index</a><br><a href="file:/home/vadim/opengnsys/opengnsys/installer/opengnsys_git_installer.py">/home/vadim/opengnsys/opengnsys/installer/opengnsys_git_installer.py</a></td></tr></table>
<p><span class="code">Script&nbsp;para&nbsp;la&nbsp;instalación&nbsp;del&nbsp;repositorio&nbsp;git</span></p>
<p>
<table class="section">
<tr class="decor pkg-content-decor heading-text">
<td class="section-title" colspan=3>&nbsp;<br><strong class="bigsection">Modules</strong></td></tr>
<tr><td class="decor pkg-content-decor"><span class="code">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></td><td>&nbsp;</td>
<td class="singlecolumn"><table><tr><td class="multicolumn"><a href="argparse.html">argparse</a><br>
<a href="git.html">git</a><br>
<a href="grp.html">grp</a><br>
</td><td class="multicolumn"><a href="libarchive.html">libarchive</a><br>
<a href="logging.html">logging</a><br>
<a href="os.html">os</a><br>
</td><td class="multicolumn"><a href="pwd.html">pwd</a><br>
<a href="shutil.html">shutil</a><br>
<a href="subprocess.html">subprocess</a><br>
</td><td class="multicolumn"><a href="sys.html">sys</a><br>
<a href="tempfile.html">tempfile</a><br>
</td></tr></table></td></tr></table><p>
<table class="section">
<tr class="decor index-decor heading-text">
<td class="section-title" colspan=3>&nbsp;<br><strong class="bigsection">Classes</strong></td></tr>
<tr><td class="decor index-decor"><span class="code">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></td><td>&nbsp;</td>
<td class="singlecolumn"><dl>
<dt class="heading-text"><a href="builtins.html#Exception">builtins.Exception</a>(<a href="builtins.html#BaseException">builtins.BaseException</a>)
</dt><dd>
<dl>
<dt class="heading-text"><a href="opengnsys_git_installer.html#RequirementException">RequirementException</a>
</dt></dl>
</dd>
<dt class="heading-text"><a href="builtins.html#object">builtins.object</a>
</dt><dd>
<dl>
<dt class="heading-text"><a href="opengnsys_git_installer.html#FakeTemporaryDirectory">FakeTemporaryDirectory</a>
</dt><dt class="heading-text"><a href="opengnsys_git_installer.html#Oglive">Oglive</a>
</dt><dt class="heading-text"><a href="opengnsys_git_installer.html#OpengnsysGitInstaller">OpengnsysGitInstaller</a>
</dt></dl>
</dd>
</dl>
<p>
<table class="section">
<tr class="decor title-decor heading-text">
<td class="section-title" colspan=3>&nbsp;<br><a name="FakeTemporaryDirectory">class <strong>FakeTemporaryDirectory</strong></a>(<a href="builtins.html#object">builtins.object</a>)</td></tr>
<tr><td class="decor title-decor" rowspan=2><span class="code">&nbsp;&nbsp;&nbsp;</span></td>
<td class="decor title-decor" colspan=2><span class="code"><a href="#FakeTemporaryDirectory">FakeTemporaryDirectory</a>(dirname)<br>
&nbsp;<br>
Imitación&nbsp;de&nbsp;TemporaryDirectory&nbsp;para&nbsp;depuración<br>&nbsp;</span></td></tr>
<tr><td>&nbsp;</td>
<td class="singlecolumn">Methods defined here:<br>
<dl><dt><a name="FakeTemporaryDirectory-__init__"><strong>__init__</strong></a>(self, dirname)</dt><dd><span class="code">Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</span></dd></dl>
<dl><dt><a name="FakeTemporaryDirectory-__str__"><strong>__str__</strong></a>(self)</dt><dd><span class="code">Return&nbsp;str(self).</span></dd></dl>
<hr>
Data descriptors defined here:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><span class="code">dictionary&nbsp;for&nbsp;instance&nbsp;variables</span></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><span class="code">list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object</span></dd>
</dl>
</td></tr></table> <p>
<table class="section">
<tr class="decor title-decor heading-text">
<td class="section-title" colspan=3>&nbsp;<br><a name="Oglive">class <strong>Oglive</strong></a>(<a href="builtins.html#object">builtins.object</a>)</td></tr>
<tr><td class="decor title-decor" rowspan=2><span class="code">&nbsp;&nbsp;&nbsp;</span></td>
<td class="decor title-decor" colspan=2><span class="code">Interfaz&nbsp;a&nbsp;utilidad&nbsp;oglivecli<br>
&nbsp;<br>
Esto&nbsp;es&nbsp;probablemente&nbsp;temporal&nbsp;hasta&nbsp;que&nbsp;se&nbsp;haga&nbsp;una&nbsp;conversión&nbsp;de&nbsp;oglivecli<br>&nbsp;</span></td></tr>
<tr><td>&nbsp;</td>
<td class="singlecolumn">Methods defined here:<br>
<dl><dt><a name="Oglive-__init__"><strong>__init__</strong></a>(self)</dt><dd><span class="code">Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</span></dd></dl>
<dl><dt><a name="Oglive-get_clients"><strong>get_clients</strong></a>(self)</dt><dd><span class="code">Devuelve&nbsp;la&nbsp;lista&nbsp;de&nbsp;clientes&nbsp;en&nbsp;un&nbsp;dict</span></dd></dl>
<dl><dt><a name="Oglive-get_default"><strong>get_default</strong></a>(self)</dt><dd><span class="code">Devuelve&nbsp;el&nbsp;cliente&nbsp;por&nbsp;defecto</span></dd></dl>
<hr>
Data descriptors defined here:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><span class="code">dictionary&nbsp;for&nbsp;instance&nbsp;variables</span></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><span class="code">list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object</span></dd>
</dl>
</td></tr></table> <p>
<table class="section">
<tr class="decor title-decor heading-text">
<td class="section-title" colspan=3>&nbsp;<br><a name="OpengnsysGitInstaller">class <strong>OpengnsysGitInstaller</strong></a>(<a href="builtins.html#object">builtins.object</a>)</td></tr>
<tr><td class="decor title-decor" rowspan=2><span class="code">&nbsp;&nbsp;&nbsp;</span></td>
<td class="decor title-decor" colspan=2><span class="code">Instalador&nbsp;de&nbsp;OpenGnsys<br>&nbsp;</span></td></tr>
<tr><td>&nbsp;</td>
<td class="singlecolumn">Methods defined here:<br>
<dl><dt><a name="OpengnsysGitInstaller-__init__"><strong>__init__</strong></a>(self)</dt><dd><span class="code">Inicializar&nbsp;clase</span></dd></dl>
<dl><dt><a name="OpengnsysGitInstaller-install"><strong>install</strong></a>(self)</dt><dd><span class="code">Instalar<br>
&nbsp;<br>
Ejecuta&nbsp;todo&nbsp;el&nbsp;proceso&nbsp;de&nbsp;instalación&nbsp;incluyendo:<br>
*&nbsp;Dependencias<br>
*&nbsp;Configuración&nbsp;de&nbsp;authorized_keys<br>
*&nbsp;Configuración&nbsp;de&nbsp;ssh<br>
*&nbsp;Creación&nbsp;de&nbsp;repositorio<br>
&nbsp;<br>
Raises:<br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#RequirementException">RequirementException</a>:&nbsp;No&nbsp;ejecutado&nbsp;por&nbsp;usuario&nbsp;root<br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#RequirementException">RequirementException</a>:&nbsp;No&nbsp;ejecutado&nbsp;en&nbsp;Debian&nbsp;o&nbsp;Ubuntu<br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#RequirementException">RequirementException</a>:&nbsp;Falta&nbsp;clave&nbsp;pública<br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#RequirementException">RequirementException</a>:&nbsp;Python&nbsp;&lt;&nbsp;3.8</span></dd></dl>
<dl><dt><a name="OpengnsysGitInstaller-set_basepath"><strong>set_basepath</strong></a>(self, value)</dt><dd><span class="code">Establece&nbsp;ruta&nbsp;base</span></dd></dl>
<dl><dt><a name="OpengnsysGitInstaller-set_ignoresshkey"><strong>set_ignoresshkey</strong></a>(self, value)</dt><dd><span class="code">Ignorar&nbsp;clave&nbsp;de&nbsp;ssh</span></dd></dl>
<dl><dt><a name="OpengnsysGitInstaller-set_testmode"><strong>set_testmode</strong></a>(self, value)</dt><dd><span class="code">Establece&nbsp;el&nbsp;modo&nbsp;de&nbsp;prueba</span></dd></dl>
<dl><dt><a name="OpengnsysGitInstaller-set_usesshkey"><strong>set_usesshkey</strong></a>(self, value)</dt><dd><span class="code">Usar&nbsp;clave&nbsp;de&nbsp;ssh</span></dd></dl>
<hr>
Data descriptors defined here:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><span class="code">dictionary&nbsp;for&nbsp;instance&nbsp;variables</span></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><span class="code">list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object</span></dd>
</dl>
</td></tr></table> <p>
<table class="section">
<tr class="decor title-decor heading-text">
<td class="section-title" colspan=3>&nbsp;<br><a name="RequirementException">class <strong>RequirementException</strong></a>(<a href="builtins.html#Exception">builtins.Exception</a>)</td></tr>
<tr><td class="decor title-decor" rowspan=2><span class="code">&nbsp;&nbsp;&nbsp;</span></td>
<td class="decor title-decor" colspan=2><span class="code"><a href="#RequirementException">RequirementException</a>(message)<br>
&nbsp;<br>
Excepción&nbsp;que&nbsp;arrojamos&nbsp;cuando&nbsp;nos&nbsp;falta&nbsp;algún&nbsp;requisito<br>&nbsp;</span></td></tr>
<tr><td>&nbsp;</td>
<td class="singlecolumn"><dl><dt>Method resolution order:</dt>
<dd><a href="opengnsys_git_installer.html#RequirementException">RequirementException</a></dd>
<dd><a href="builtins.html#Exception">builtins.Exception</a></dd>
<dd><a href="builtins.html#BaseException">builtins.BaseException</a></dd>
<dd><a href="builtins.html#object">builtins.object</a></dd>
</dl>
<hr>
Methods defined here:<br>
<dl><dt><a name="RequirementException-__init__"><strong>__init__</strong></a>(self, message)</dt><dd><span class="code">Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</span></dd></dl>
<hr>
Data descriptors defined here:<br>
<dl><dt><strong>__weakref__</strong></dt>
<dd><span class="code">list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object</span></dd>
</dl>
<hr>
Static methods inherited from <a href="builtins.html#Exception">builtins.Exception</a>:<br>
<dl><dt><a name="RequirementException-__new__"><strong>__new__</strong></a>(*args, **kwargs)<span class="grey"><span class="heading-text"> from <a href="builtins.html#type">builtins.type</a></span></span></dt><dd><span class="code">Create&nbsp;and&nbsp;return&nbsp;a&nbsp;new&nbsp;<a href="builtins.html#object">object</a>.&nbsp;&nbsp;See&nbsp;help(type)&nbsp;for&nbsp;accurate&nbsp;signature.</span></dd></dl>
<hr>
Methods inherited from <a href="builtins.html#BaseException">builtins.BaseException</a>:<br>
<dl><dt><a name="RequirementException-__delattr__"><strong>__delattr__</strong></a>(self, name, /)</dt><dd><span class="code">Implement&nbsp;delattr(self,&nbsp;name).</span></dd></dl>
<dl><dt><a name="RequirementException-__getattribute__"><strong>__getattribute__</strong></a>(self, name, /)</dt><dd><span class="code">Return&nbsp;getattr(self,&nbsp;name).</span></dd></dl>
<dl><dt><a name="RequirementException-__reduce__"><strong>__reduce__</strong></a>(...)</dt><dd><span class="code">Helper&nbsp;for&nbsp;pickle.</span></dd></dl>
<dl><dt><a name="RequirementException-__repr__"><strong>__repr__</strong></a>(self, /)</dt><dd><span class="code">Return&nbsp;repr(self).</span></dd></dl>
<dl><dt><a name="RequirementException-__setattr__"><strong>__setattr__</strong></a>(self, name, value, /)</dt><dd><span class="code">Implement&nbsp;setattr(self,&nbsp;name,&nbsp;value).</span></dd></dl>
<dl><dt><a name="RequirementException-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
<dl><dt><a name="RequirementException-__str__"><strong>__str__</strong></a>(self, /)</dt><dd><span class="code">Return&nbsp;str(self).</span></dd></dl>
<dl><dt><a name="RequirementException-add_note"><strong>add_note</strong></a>(...)</dt><dd><span class="code"><a href="builtins.html#Exception">Exception</a>.<a href="#RequirementException-add_note">add_note</a>(note)&nbsp;--<br>
add&nbsp;a&nbsp;note&nbsp;to&nbsp;the&nbsp;exception</span></dd></dl>
<dl><dt><a name="RequirementException-with_traceback"><strong>with_traceback</strong></a>(...)</dt><dd><span class="code"><a href="builtins.html#Exception">Exception</a>.<a href="#RequirementException-with_traceback">with_traceback</a>(tb)&nbsp;--<br>
set&nbsp;self.<strong>__traceback__</strong>&nbsp;to&nbsp;tb&nbsp;and&nbsp;return&nbsp;self.</span></dd></dl>
<hr>
Data descriptors inherited from <a href="builtins.html#BaseException">builtins.BaseException</a>:<br>
<dl><dt><strong>__cause__</strong></dt>
<dd><span class="code">exception&nbsp;cause</span></dd>
</dl>
<dl><dt><strong>__context__</strong></dt>
<dd><span class="code">exception&nbsp;context</span></dd>
</dl>
<dl><dt><strong>__dict__</strong></dt>
</dl>
<dl><dt><strong>__suppress_context__</strong></dt>
</dl>
<dl><dt><strong>__traceback__</strong></dt>
</dl>
<dl><dt><strong>args</strong></dt>
</dl>
</td></tr></table></td></tr></table><p>
<table class="section">
<tr class="decor functions-decor heading-text">
<td class="section-title" colspan=3>&nbsp;<br><strong class="bigsection">Functions</strong></td></tr>
<tr><td class="decor functions-decor"><span class="code">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></td><td>&nbsp;</td>
<td class="singlecolumn"><dl><dt><a name="-show_error"><strong>show_error</strong></a>(*args)</dt></dl>
</td></tr></table>
</body></html>

View File

@ -0,0 +1,370 @@
#!/usr/bin/env python3
"""Script para la instalación del repositorio git"""
import os
import shutil
import argparse
import tempfile
import logging
import subprocess
import sys
import pwd
import grp
from termcolor import colored, cprint
import git
import libarchive
def show_error(*args):
"""
Imprime un mensaje de error
Args:
*args: Argumentos igual que a la función print
Returns:
None
"""
cprint(*args, "red", attrs = ["bold"], file=sys.stderr)
class RequirementException(Exception):
"""Excepción que indica que nos falta algún requisito
Attributes:
message (str): Mensaje de error mostrado al usuario
"""
def __init__(self, message):
"""Inicializar RequirementException.
Args:
message (str): Mensaje de error mostrado al usuario
"""
super().__init__(message)
self.message = message
class FakeTemporaryDirectory:
"""Imitación de TemporaryDirectory para depuración"""
def __init__(self, dirname):
self.name = dirname
os.makedirs(dirname, exist_ok=True)
def __str__(self):
return self.name
class Oglive:
"""Interfaz a utilidad oglivecli
Esto es probablemente temporal hasta que se haga una conversión de oglivecli
"""
def __init__(self):
self.__logger = logging.getLogger("Oglive")
self.binary = "/opt/opengnsys/bin/oglivecli"
self.__logger.debug("Inicializando")
def _cmd(self, args):
cmd = [self.binary] + args
self.__logger.debug("comando: %s", cmd)
proc = subprocess.run(cmd, shell=False, check=True, capture_output=True)
out_text = proc.stdout.decode('utf-8').strip()
self.__logger.debug("salida: %s", out_text)
return out_text
def get_default(self):
"""Devuelve el cliente por defecto"""
self.__logger.debug("get_default()")
return self._cmd(["get-default"])
def get_clients(self):
"""Devuelve la lista de clientes en un dict"""
self.__logger.debug("get_clients()")
lines = self._cmd(["list"]).splitlines()
clients = {}
for line in lines:
(number, name) = line.split()
clients[number] = name
self.__logger.debug("Clientes: %s", clients)
return clients
class OpengnsysGitInstaller:
"""Instalador de OpenGnsys"""
def __init__(self):
"""Inicializar clase"""
self.__logger = logging.getLogger("OpengnsysGitInstaller")
self.__logger.setLevel(logging.DEBUG)
self.__logger.debug("Inicializando")
self.testmode = False
self.base_path = "/opt/opengnsys"
self.git_basedir = "base.git"
self.ssh_user = "opengnsys"
self.ssh_group = "opengnsys"
self.ssh_homedir = pwd.getpwnam(self.ssh_user).pw_dir
self.ssh_uid = pwd.getpwnam(self.ssh_user).pw_uid
self.ssh_gid = grp.getgrnam(self.ssh_group).gr_gid
self.temp_dir = None
# Possible names for SSH key
self.key_paths = ["scripts/ssl/id_rsa.pub", "scripts/ssl/id_ed25519.pub", "scripts/ssl/id_ecdsa.pub", "scripts/ssl/id_ed25519_sk.pub", "scripts/ssl/id_ecdsa_sk.pub"]
self.key_paths_dict = {}
for kp in self.key_paths:
self.key_paths_dict[kp] = 1
self.oglive = Oglive()
def set_testmode(self, value):
"""Establece el modo de prueba"""
self.testmode = value
def set_ignoresshkey(self, value):
"""Ignorar requisito de clave de ssh para el instalador"""
self.ignoresshkey = value
def set_usesshkey(self, value):
"""Usar clave de ssh especificada"""
self.usesshkey = value
def set_basepath(self, value):
"""Establece ruta base de OpenGnsys
Valor por defecto: /opt/opengnsys
"""
self.base_path = value
def _get_tempdir(self):
"""Obtiene el directorio temporal"""
if self.testmode:
dirname = "/tmp/ogtemp"
if os.path.exists(dirname):
shutil.rmtree(dirname)
dir=FakeTemporaryDirectory(dirname)
self.__logger.debug("Modo de prueba, temp=/tmp/ogtemp")
return dir
else:
dir = tempfile.TemporaryDirectory()
self.__logger.debug("Temp = %s", dir)
return dir
def _cleanup(self):
"""Limpia el directorio temporal"""
if self.temp_dir:
shutil.rmtree(self.temp_dir, ignore_errors=True)
def _init_git_repo(self, reponame):
"""Inicializa un repositorio Git"""
# Creamos repositorio
ogdir_images = os.path.join(self.base_path, "images")
self.__logger.info("Creando repositorio de GIT %s", reponame)
os.makedirs(os.path.join(ogdir_images, self.git_basedir), exist_ok=True)
repo_path=os.path.join(ogdir_images, reponame)
shutil.rmtree(repo_path, ignore_errors=True)
# Marcar como directorio seguro
# Nota: no usar GitPython. Config global falla, aunque hay indicaciones de que
# git.Repo(path=None) es valido. Posiblemente bug de GitPython.
subprocess.run(["git", "config", "--global", "add" "safe.directory", repo_path])
self.__logger.debug("Inicializando repositorio: " + repo_path)
repo = git.Repo.init(repo_path, bare = True)
self.__logger.info("Configurando repositorio de GIT")
repo.config_writer().set_value("user", "name", "OpenGnsys").release()
repo.config_writer().set_value("user", "email", "OpenGnsys@opengnsys.com").release()
def _add_line_to_file(self, filename, new_line):
"""Agrega una línea a un archivo"""
found = False
self.__logger.debug("Agregando linea: %s a %s", new_line, filename)
with open(filename, "a+", encoding="utf-8") as f:
f.seek(0)
for line in f:
if line.strip() == new_line.strip():
found = True
if not found:
self.__logger.debug("Agregando linea: %s", new_line)
f.write(new_line + "\n")
else:
self.__logger.debug("Linea ya presente")
def _recursive_chown(self, path, ouid, ogid):
"""Cambia el propietario y grupo de forma recursiva"""
for dirpath, _, filenames in os.walk(path):
os.chown(dirpath, uid=ouid, gid=ogid)
for filename in filenames:
os.chown(os.path.join(dirpath, filename), uid=ouid, gid=ogid)
def install(self):
"""Instalar
Ejecuta todo el proceso de instalación incluyendo:
* Dependencias
* Configuración de authorized_keys
* Configuración de ssh
* Creación de repositorio
Raises:
RequirementException: No ejecutado por usuario root
RequirementException: No ejecutado en Debian o Ubuntu
RequirementException: Falta clave pública
RequirementException: Python < 3.8
"""
self.__logger.info("install()")
ogdir_images = os.path.join(self.base_path, "images")
ENGINECFG = os.path.join(self.base_path, "client/etc/engine.cfg")
os.environ["PATH"] += os.pathsep + os.path.join(self.base_path, "bin")
tftp_dir = os.path.join(self.base_path, "tftpboot")
INITRD = "oginitrd.img"
self.temp_dir = self._get_tempdir()
SSHUSER = "opengnsys"
# Control básico de errores.
self.__logger.debug("Comprobando euid")
if os.geteuid() != 0:
raise RequirementException("Sólo ejecutable por root")
if not os.path.exists("/etc/debian_version"):
raise RequirementException("Instalación sólo soportada en Debian y Ubuntu")
MIN_PYTHON = (3, 8)
if sys.version_info < MIN_PYTHON:
raise RequirementException(f"Python %s.%s mínimo requerido.\n" % MIN_PYTHON)
self.__logger.debug("Instalando dependencias")
subprocess.run(["apt-get", "install", "-y", "git"], check=True)
# Autenticación del usuario opengnsys con clave pública desde los ogLive
# Requiere que todos los ogLive tengan la misma clave publica (utilizar setsslkey)
# Tomamos la clave publica del cliente por defecto
default_num = self.oglive.get_default()
default_client = self.oglive.get_clients()[default_num]
client_initrd_path = os.path.join(tftp_dir, default_client, INITRD)
self.__logger.debug("Ruta de initrd: %s", client_initrd_path)
# Si me salgo con error borro el directorio temporal
if not self.ignoresshkey:
public_key=""
if self.usesshkey:
with open(self.usesshkey, 'r') as f:
public_key = f.read().strip()
else:
if os.path.isfile(client_initrd_path):
#os.makedirs(temp_dir, exist_ok=True)
os.chdir(self.temp_dir.name)
self.__logger.debug("Descomprimiendo %s", client_initrd_path)
public_key = None
with libarchive.file_reader(client_initrd_path) as initrd:
for file in initrd:
self.__logger.debug("Archivo: %s", file)
if file.pathname in self.key_paths_dict:
data = bytearray()
for block in file.get_blocks():
data = data + block
public_key = data.decode('utf-8').strip()
break
else:
print(f"No se encuentra la imagen de initrd {client_initrd_path}")
exit(2)
# Si la clave publica no existe me salgo con error
if not public_key:
raise RequirementException(f"No se encuentra clave pública dentro del ogLive en {self.temp_dir}, imagen {client_initrd_path}. Rutas buscadas: {self.key_paths}\n" +
"Los oglive deben tener la misma clave pública (utilizar setsslkey)")
ssh_dir = os.path.join(self.ssh_homedir, ".ssh")
authorized_keys_file = os.path.join(ssh_dir, "authorized_keys")
self.__logger.debug("Configurando ssh: Agregando clave %s a %s", public_key, authorized_keys_file)
self.__logger.debug("Key: %s", public_key)
os.makedirs(ssh_dir, exist_ok=True)
self._add_line_to_file(authorized_keys_file, public_key)
os.chmod(authorized_keys_file, 0o600)
os.chown(ssh_dir, uid=self.ssh_uid, gid=self.ssh_gid)
os.chown(authorized_keys_file, uid=self.ssh_uid, gid=self.ssh_gid)
# Configuramos el servicio ssh para que permita el acceso con clave pública
self.__logger.info(" Configuramos el servicio ssh para que permita el acceso con clave pública.")
with open("/etc/ssh/sshd_config", "r") as f:
sshd_config = f.read()
sshd_config = sshd_config.replace("PubkeyAuthentication no", "PubkeyAuthentication yes")
with open("/etc/ssh/sshd_config", "w") as f:
f.write(sshd_config)
os.system("systemctl reload ssh")
# Instalamos git
os.system("apt install git")
# Para que el usuario sólo pueda usar git (no ssh)
SHELL = shutil.which("git-shell")
os.system(f"usermod -s {SHELL} opengnsys")
# Creamos repositorios
self._init_git_repo('windows.git')
self._init_git_repo('linux.git')
self._init_git_repo('mac.git')
# Damos permiso al usuario opengnsys
for DIR in ["base.git", "linux.git", "windows.git"]: #, "LinAcl", "WinAcl"]:
self._recursive_chown(os.path.join(ogdir_images, DIR), ouid=self.ssh_uid, ogid=self.ssh_gid)
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)20s - [%(levelname)5s] - %(message)s')
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logger.info("Inicio del programa")
parser = argparse.ArgumentParser(
prog="OpenGnsys Installer",
description="Script para la instalación del repositorio git",
)
parser.add_argument('--testmode', action='store_true', help="Modo de prueba")
parser.add_argument('--ignoresshkey', action='store_true', help="Ignorar clave de SSH")
parser.add_argument('--usesshkey', type=str, help="Usar clave SSH especificada")
args = parser.parse_args()
installer = OpengnsysGitInstaller()
installer.set_testmode(args.testmode)
installer.set_ignoresshkey(args.ignoresshkey)
installer.set_usesshkey(args.usesshkey)
logger.debug("Inicio de instalación")
try:
installer.install()
except RequirementException as req:
show_error(f"Requisito para la instalación no satisfecho: {req.message}")
exit(1)

View File

@ -0,0 +1,6 @@
gitdb==4.0.11
GitPython==3.1.43
libarchive-c==5.1
nose==1.3.7
smmap==5.0.1
termcolor==2.4.0