|
|
|
@ -12,25 +12,28 @@
|
|
|
|
|
#
|
|
|
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
import logging
|
|
|
|
|
import os
|
|
|
|
|
import sh
|
|
|
|
|
import shutil
|
|
|
|
|
import yaml
|
|
|
|
|
import requests
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
from threading import Thread, Event
|
|
|
|
|
from threading import Thread
|
|
|
|
|
from time import sleep
|
|
|
|
|
|
|
|
|
|
from django.utils.crypto import get_random_string
|
|
|
|
|
from django.template.loader import render_to_string
|
|
|
|
|
from django.core.mail import send_mail
|
|
|
|
|
from django.conf import settings
|
|
|
|
|
from git import Repo, Commit
|
|
|
|
|
from git.exc import InvalidGitRepositoryError
|
|
|
|
|
|
|
|
|
|
from dash.models import Instance
|
|
|
|
|
|
|
|
|
|
from infrastructure.models import InstanceCreated, JobType, Job
|
|
|
|
|
|
|
|
|
|
logging.basicConfig()
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Worker(Thread):
|
|
|
|
|
def __init__(self, job: Job):
|
|
|
|
@ -75,7 +78,7 @@ class Worker(Thread):
|
|
|
|
|
job.delete()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_vm_if_not_exists(instance: Instance) -> (str, Commit):
|
|
|
|
|
def create_vm_if_not_exists(instance: Instance) -> (str, str):
|
|
|
|
|
"""
|
|
|
|
|
Create VM utility. Gitea password is returned
|
|
|
|
|
"""
|
|
|
|
@ -111,18 +114,17 @@ class Infra:
|
|
|
|
|
def __init__(self):
|
|
|
|
|
conf = settings.HOSTEA["INFRA"]["HOSTEA_REPO"]
|
|
|
|
|
self.repo_path = Path(conf["PATH"])
|
|
|
|
|
if not self.repo_path.exists():
|
|
|
|
|
os.makedirs(self.repo_path)
|
|
|
|
|
|
|
|
|
|
ssh_cmd = f"/usr/bin/ssh -oStrictHostKeyChecking=no -i {conf['SSH_KEY']}"
|
|
|
|
|
self.env = {"GIT_SSH_COMMAND": ssh_cmd}
|
|
|
|
|
self._clone()
|
|
|
|
|
|
|
|
|
|
def _clone(self):
|
|
|
|
|
conf = settings.HOSTEA["INFRA"]["HOSTEA_REPO"]
|
|
|
|
|
ssh_cmd = f"/usr/bin/ssh -oStrictHostKeyChecking=no -i {conf['SSH_KEY']}"
|
|
|
|
|
self.git = sh.git.bake(_env={"GIT_SSH_COMMAND": ssh_cmd})
|
|
|
|
|
conf = settings.HOSTEA["INFRA"]["HOSTEA_REPO"]
|
|
|
|
|
if os.path.exists(self.repo_path):
|
|
|
|
|
shutil.rmtree(self.repo_path)
|
|
|
|
|
self.repo = Repo.clone_from(conf["REMOTE"], self.repo_path, env=self.env)
|
|
|
|
|
self.git.clone(conf["REMOTE"], self.repo_path)
|
|
|
|
|
self.git = self.git.bake("-C", self.repo_path)
|
|
|
|
|
|
|
|
|
|
def _host_vars_dir(self, subdomain: str) -> Path:
|
|
|
|
|
"""
|
|
|
|
@ -210,25 +212,21 @@ class Infra:
|
|
|
|
|
f.write(content)
|
|
|
|
|
f.write("\n")
|
|
|
|
|
|
|
|
|
|
def _add_files(self, subdomain: str):
|
|
|
|
|
"""
|
|
|
|
|
Add all relevant files of a VM
|
|
|
|
|
"""
|
|
|
|
|
self.repo.git.add(str(self._host_vars_dir(subdomain=subdomain)))
|
|
|
|
|
self.repo.git.add(str(self._backup_path(subdomain=subdomain)))
|
|
|
|
|
self.repo.git.add(str(self._service_path(subdomain=subdomain)))
|
|
|
|
|
self.repo.git.add(str(self._hostscript_path(subdomain=subdomain)))
|
|
|
|
|
|
|
|
|
|
def _commit(self, action: str, subdomain: str) -> Commit:
|
|
|
|
|
"""
|
|
|
|
|
Commit changes to a VM configuration
|
|
|
|
|
"""
|
|
|
|
|
def _push(self, message):
|
|
|
|
|
self.git.add(".")
|
|
|
|
|
self.git.config("user.email", settings.HOSTEA["INSTANCE_MAINTAINER_CONTACT"])
|
|
|
|
|
self.git.config("user.name", "Hostea dashboard")
|
|
|
|
|
try:
|
|
|
|
|
self.git.commit("-m", f"dashboard: {message}")
|
|
|
|
|
except sh.ErrorReturnCode_1:
|
|
|
|
|
logger.debug("no change")
|
|
|
|
|
else:
|
|
|
|
|
self.git.push("origin", "master")
|
|
|
|
|
return self._sha()
|
|
|
|
|
|
|
|
|
|
self._add_files(subdomain=subdomain)
|
|
|
|
|
return self.repo.git.commit(
|
|
|
|
|
message=f"{action} VM {subdomain}",
|
|
|
|
|
author="Dashboard Bot <bot@dashboard.hostea.org>",
|
|
|
|
|
)
|
|
|
|
|
def _sha(self):
|
|
|
|
|
sha = self.git("rev-parse", "origin/master")
|
|
|
|
|
return str(sha).strip()
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def translate_size(instance: Instance) -> str:
|
|
|
|
@ -244,13 +242,12 @@ class Infra:
|
|
|
|
|
return "openstack_flavor_large"
|
|
|
|
|
return instance.configuration_id.name
|
|
|
|
|
|
|
|
|
|
def add_vm(self, instance: Instance) -> (str, Commit):
|
|
|
|
|
def add_vm(self, instance: Instance) -> (str, str):
|
|
|
|
|
"""
|
|
|
|
|
Add new VM to infrastructure repository
|
|
|
|
|
|
|
|
|
|
The gitea user password is returned
|
|
|
|
|
"""
|
|
|
|
|
self._clone()
|
|
|
|
|
|
|
|
|
|
subdomain = instance.name
|
|
|
|
|
host_vars_dir = self._host_vars_dir(subdomain)
|
|
|
|
@ -321,29 +318,26 @@ class Infra:
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
commit = self._commit(action="add", subdomain=subdomain)
|
|
|
|
|
self.repo.git.push(env=self.env)
|
|
|
|
|
commit = self._push(f"add vm {subdomain}")
|
|
|
|
|
return (gitea_password, commit)
|
|
|
|
|
|
|
|
|
|
def remove_vm(self, instance: Instance):
|
|
|
|
|
"""
|
|
|
|
|
Remove a VM from infrastructure repository
|
|
|
|
|
"""
|
|
|
|
|
self._clone()
|
|
|
|
|
subdomain = instance.name
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
|
|
host_vars_dir = self._host_vars_dir(subdomain)
|
|
|
|
|
host_vars_dir = self._host_vars_dir(subdomain)
|
|
|
|
|
if os.path.exists(host_vars_dir):
|
|
|
|
|
shutil.rmtree(host_vars_dir)
|
|
|
|
|
|
|
|
|
|
backup = self._backup_path(subdomain)
|
|
|
|
|
backup = self._backup_path(subdomain)
|
|
|
|
|
if os.path.exists(backup):
|
|
|
|
|
os.remove(backup)
|
|
|
|
|
|
|
|
|
|
service = self._service_path(subdomain)
|
|
|
|
|
service = self._service_path(subdomain)
|
|
|
|
|
if os.path.exists(service):
|
|
|
|
|
os.remove(service)
|
|
|
|
|
except FileNotFoundError:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
hostscript = self._hostscript_path(subdomain)
|
|
|
|
|
with open(hostscript, "w+", encoding="utf-8") as f:
|
|
|
|
@ -356,5 +350,4 @@ class Infra:
|
|
|
|
|
context={"subdomain": subdomain},
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
self._commit(action="rm", subdomain=subdomain)
|
|
|
|
|
self.repo.git.push(env=self.env)
|
|
|
|
|
return self._push(f"rm vm {subdomain}")
|
|
|
|
|