dashboard/infrastructure/utils.py

221 lines
7.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# Copyright © 2022 Aravinth Manivannan <realaravinth@batsense.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# 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 os
import shutil
from pathlib import Path
from django.utils.crypto import get_random_string
from django.template.loader import render_to_string
from django.conf import settings
from git import Repo
from git.exc import InvalidGitRepositoryError
from dash.models import Instance
class Infra:
"""
Utility function to manage infrastructure repository
"""
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)
self.ssh_cmd = f"ssh -i {conf['SSH_KEY']}"
try:
self.repo = Repo(path=self.repo_path)
except InvalidGitRepositoryError:
self.repo = Repo.clone_from(
conf["REMOTE"], self.repo_path, env={"GIT_SSH_COMMAND": self.ssh_cmd}
)
def _host_vars_dir(self, subdomain: str) -> Path:
"""
utility method: get host_vars directory for a subdomain
"""
return self.repo_path.joinpath(f"inventory/host_vars/{subdomain}-host/")
def _provision_path(self, subdomain: str) -> Path:
"""
utility method: get provision file pay for a subdomain
"""
return self._host_vars_dir(subdomain=subdomain).joinpath("provision.yml")
def _gitea_path(self, subdomain: str) -> Path:
"""
utility method: get gitea file for a subdomain
"""
return self._host_vars_dir(subdomain=subdomain).joinpath("gitea.yml")
def _backup_path(self, subdomain: str) -> Path:
"""
utility method: get backup file for a subdomain
"""
return self.repo_path.joinpath(f"inventory/{subdomain}-backup.yml")
def _hostscript_path(self, subdomain: str) -> Path:
"""
utility method: hostscript file for a subdomain
"""
return self.repo_path.joinpath(f"inventory/hosts-scripts/{subdomain}-host.sh")
def write_hostscript(self, subdomain: str, content: str):
"""
Write contents to hostscript.
Hostscript will contain the history of all actions that have been
ordered on a particular VM. So content needs to be appended to it,
rather than overwritten.
"""
hostscript = self._hostscript_path(subdomain)
with open(hostscript, "a", encoding="utf-8") as f:
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._hostscript_path(subdomain=subdomain)))
def _commit(self, action: str, subdomain: str):
"""
Commit changes to a VM configuration
"""
self._add_files(subdomain=subdomain)
self.repo.git.commit(
message=f"{action} VM {subdomain}",
author="Dashboard Bot <bot@dashboard.hostea.org>",
)
def add_vm(self, instance: Instance) -> str:
"""
Add new VM to infrastructure repository
The gitea user password is returned
"""
self.repo.git.pull()
subdomain = instance.name
host_vars_dir = self._host_vars_dir(subdomain)
if not host_vars_dir.exists():
os.makedirs(host_vars_dir)
hostscript_path = self.repo_path.joinpath("inventory/hosts-scripts/")
if not hostscript_path.exists():
os.makedirs(hostscript_path)
woodpecker_agent_secret = get_random_string(64)
gitea_password = get_random_string(20)
ctx = {
"woodpecker_agent_secret": woodpecker_agent_secret,
"woodpecker_hostname": f"{subdomain}-ci",
"woodpecker_admins": f"{instance.owned_by.username}",
"gitea_email": instance.owned_by.email,
"gitea_password": gitea_password,
"subdomain": subdomain,
}
gitea = self._gitea_path(subdomain)
with open(gitea, "w", encoding="utf-8") as f:
f.write(
render_to_string(
"infrastructure/yml/gitea.yml",
context=ctx,
)
)
# provision_template = "./templates/infrastructure/yml/provision.yml"
provision = self._provision_path(subdomain)
# TODO: instance config names are different the flavours expected:
# ```
# openstack_flavor: {{ openstack_flavor_medium }} * openstack_flavor: {{ openstack_flavor_large }}
# ```
# check with @dachary about this
size = None
if instance.configuration_id.name == "s1-2":
size = "openstack_flavor_small"
elif instance.configuration_id.name == "s1-4":
size = "openstack_flavor_medium"
elif instance.configuration_id.name == "s1-8":
size = "openstack_flavor_large"
else:
size = instance.configuration_id.name
with open(provision, "w", encoding="utf-8") as f:
f.write(
render_to_string(
"infrastructure/yml/provision.yml", context={"vm_size": size}
)
)
# backup = self.repo_path.joinpath(f"inventory/{instance.name}-backup.yml")
backup = self._backup_path(subdomain)
# backup_template = "./templates/infrastructure/yml/provision.yml"
with open(backup, "w", encoding="utf-8") as f:
f.write(
render_to_string(
"infrastructure/yml/backups.yml", context={"subdomain": subdomain}
)
)
# hostscript = self.repo_path.join("inventory/hosts-scripts/{instance.name}-host.sh")
self.write_hostscript(
subdomain=subdomain,
content=render_to_string(
"infrastructure/sh/hostscripts/create.sh",
context={"subdomain": subdomain},
),
)
self._commit(action="add", subdomain=subdomain)
self.repo.git.push()
return gitea_password
def remove_vm(self, instance: Instance):
"""
Remove a VM from infrastructure repository
"""
self.repo.git.pull()
subdomain = instance.name
host_vars_dir = self._host_vars_dir(subdomain)
shutil.rmtree(host_vars_dir)
backup = self._backup_path(subdomain)
os.remove(backup)
self.write_hostscript(
subdomain=subdomain,
content=render_to_string(
"infrastructure/sh/hostscripts/rm.sh",
context={"subdomain": subdomain},
),
)
self._commit(action="rm", subdomain=subdomain)
self.repo.git.push()