refs #734 Merge branch 'main' of https://ognproject.evlt.uma.es/gitea/opengnsys/oggit
commit
981d0405f0
|
@ -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
|
|
@ -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()
|
||||
|
||||
|
||||
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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"> <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 para la instalación del repositorio git</span></p>
|
||||
<p>
|
||||
<table class="section">
|
||||
<tr class="decor pkg-content-decor heading-text">
|
||||
<td class="section-title" colspan=3> <br><strong class="bigsection">Modules</strong></td></tr>
|
||||
|
||||
<tr><td class="decor pkg-content-decor"><span class="code"> </span></td><td> </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> <br><strong class="bigsection">Classes</strong></td></tr>
|
||||
|
||||
<tr><td class="decor index-decor"><span class="code"> </span></td><td> </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> <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"> </span></td>
|
||||
<td class="decor title-decor" colspan=2><span class="code"><a href="#FakeTemporaryDirectory">FakeTemporaryDirectory</a>(dirname)<br>
|
||||
<br>
|
||||
Imitación de TemporaryDirectory para depuración<br> </span></td></tr>
|
||||
<tr><td> </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 self. See help(type(self)) for accurate signature.</span></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeTemporaryDirectory-__str__"><strong>__str__</strong></a>(self)</dt><dd><span class="code">Return str(self).</span></dd></dl>
|
||||
|
||||
<hr>
|
||||
Data descriptors defined here:<br>
|
||||
<dl><dt><strong>__dict__</strong></dt>
|
||||
<dd><span class="code">dictionary for instance variables</span></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><span class="code">list of weak references to the object</span></dd>
|
||||
</dl>
|
||||
</td></tr></table> <p>
|
||||
<table class="section">
|
||||
<tr class="decor title-decor heading-text">
|
||||
<td class="section-title" colspan=3> <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"> </span></td>
|
||||
<td class="decor title-decor" colspan=2><span class="code">Interfaz a utilidad oglivecli<br>
|
||||
<br>
|
||||
Esto es probablemente temporal hasta que se haga una conversión de oglivecli<br> </span></td></tr>
|
||||
<tr><td> </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 self. See help(type(self)) for accurate signature.</span></dd></dl>
|
||||
|
||||
<dl><dt><a name="Oglive-get_clients"><strong>get_clients</strong></a>(self)</dt><dd><span class="code">Devuelve la lista de clientes en un dict</span></dd></dl>
|
||||
|
||||
<dl><dt><a name="Oglive-get_default"><strong>get_default</strong></a>(self)</dt><dd><span class="code">Devuelve el cliente por defecto</span></dd></dl>
|
||||
|
||||
<hr>
|
||||
Data descriptors defined here:<br>
|
||||
<dl><dt><strong>__dict__</strong></dt>
|
||||
<dd><span class="code">dictionary for instance variables</span></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><span class="code">list of weak references to the object</span></dd>
|
||||
</dl>
|
||||
</td></tr></table> <p>
|
||||
<table class="section">
|
||||
<tr class="decor title-decor heading-text">
|
||||
<td class="section-title" colspan=3> <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"> </span></td>
|
||||
<td class="decor title-decor" colspan=2><span class="code">Instalador de OpenGnsys<br> </span></td></tr>
|
||||
<tr><td> </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 clase</span></dd></dl>
|
||||
|
||||
<dl><dt><a name="OpengnsysGitInstaller-install"><strong>install</strong></a>(self)</dt><dd><span class="code">Instalar<br>
|
||||
<br>
|
||||
Ejecuta todo el proceso de instalación incluyendo:<br>
|
||||
* Dependencias<br>
|
||||
* Configuración de authorized_keys<br>
|
||||
* Configuración de ssh<br>
|
||||
* Creación de repositorio<br>
|
||||
<br>
|
||||
Raises:<br>
|
||||
<a href="#RequirementException">RequirementException</a>: No ejecutado por usuario root<br>
|
||||
<a href="#RequirementException">RequirementException</a>: No ejecutado en Debian o Ubuntu<br>
|
||||
<a href="#RequirementException">RequirementException</a>: Falta clave pública<br>
|
||||
<a href="#RequirementException">RequirementException</a>: Python < 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 ruta base</span></dd></dl>
|
||||
|
||||
<dl><dt><a name="OpengnsysGitInstaller-set_ignoresshkey"><strong>set_ignoresshkey</strong></a>(self, value)</dt><dd><span class="code">Ignorar clave de ssh</span></dd></dl>
|
||||
|
||||
<dl><dt><a name="OpengnsysGitInstaller-set_testmode"><strong>set_testmode</strong></a>(self, value)</dt><dd><span class="code">Establece el modo de prueba</span></dd></dl>
|
||||
|
||||
<dl><dt><a name="OpengnsysGitInstaller-set_usesshkey"><strong>set_usesshkey</strong></a>(self, value)</dt><dd><span class="code">Usar clave de ssh</span></dd></dl>
|
||||
|
||||
<hr>
|
||||
Data descriptors defined here:<br>
|
||||
<dl><dt><strong>__dict__</strong></dt>
|
||||
<dd><span class="code">dictionary for instance variables</span></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><span class="code">list of weak references to the object</span></dd>
|
||||
</dl>
|
||||
</td></tr></table> <p>
|
||||
<table class="section">
|
||||
<tr class="decor title-decor heading-text">
|
||||
<td class="section-title" colspan=3> <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"> </span></td>
|
||||
<td class="decor title-decor" colspan=2><span class="code"><a href="#RequirementException">RequirementException</a>(message)<br>
|
||||
<br>
|
||||
Excepción que arrojamos cuando nos falta algún requisito<br> </span></td></tr>
|
||||
<tr><td> </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 self. See help(type(self)) for accurate signature.</span></dd></dl>
|
||||
|
||||
<hr>
|
||||
Data descriptors defined here:<br>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><span class="code">list of weak references to the 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 and return a new <a href="builtins.html#object">object</a>. See help(type) for accurate 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 delattr(self, name).</span></dd></dl>
|
||||
|
||||
<dl><dt><a name="RequirementException-__getattribute__"><strong>__getattribute__</strong></a>(self, name, /)</dt><dd><span class="code">Return getattr(self, name).</span></dd></dl>
|
||||
|
||||
<dl><dt><a name="RequirementException-__reduce__"><strong>__reduce__</strong></a>(...)</dt><dd><span class="code">Helper for pickle.</span></dd></dl>
|
||||
|
||||
<dl><dt><a name="RequirementException-__repr__"><strong>__repr__</strong></a>(self, /)</dt><dd><span class="code">Return repr(self).</span></dd></dl>
|
||||
|
||||
<dl><dt><a name="RequirementException-__setattr__"><strong>__setattr__</strong></a>(self, name, value, /)</dt><dd><span class="code">Implement setattr(self, name, 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 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) --<br>
|
||||
add a note to the 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) --<br>
|
||||
set self.<strong>__traceback__</strong> to tb and return 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 cause</span></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__context__</strong></dt>
|
||||
<dd><span class="code">exception 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> <br><strong class="bigsection">Functions</strong></td></tr>
|
||||
|
||||
<tr><td class="decor functions-decor"><span class="code"> </span></td><td> </td>
|
||||
<td class="singlecolumn"><dl><dt><a name="-show_error"><strong>show_error</strong></a>(*args)</dt></dl>
|
||||
</td></tr></table>
|
||||
</body></html>
|
|
@ -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)
|
||||
|
||||
|
|
@ -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
|
Loading…
Reference in New Issue