feat: utilities to add and remove VM on the Hostea repo

pull/1/head
Aravinth Manivannan 2022-06-24 20:35:32 +05:30
parent 1a234d402f
commit f3324579c9
Signed by: realaravinth
GPG Key ID: AD9F0F08E855ED88
2 changed files with 257 additions and 0 deletions

76
infrastructure/tests.py Normal file
View File

@ -0,0 +1,76 @@
# 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/>.
from pathlib import Path
from django.test import TestCase, Client, override_settings
from django.conf import settings
from dash.models import Instance
from .utils import Infra
from accounts.tests import register_util, login_util
from dash.tests import create_configurations, create_instance_util
class InfraUtilTest(TestCase):
"""
Tests billing system
"""
def setUp(self):
self.username = "infrautil_user"
register_util(t=self, username=self.username)
create_configurations(t=self)
@override_settings(
HOSTEA={
"INFRA": {
"HOSTEA_REPO": {
"PATH": "/tmp/hostea/dashboard/test_path_util/repo/",
"REMOTE": "git@gitea.hostea.org:Hostea/payments.git",
"SSH_KEY": "/src/atm/.ssh/id",
}
}
}
)
def test_path_utils(self):
infra = Infra()
subdomain = "foo"
base = infra.repo_path
self.assertEqual(
base.joinpath(f"inventory/host_vars/{subdomain}-host/"),
infra._host_vars_dir(subdomain=subdomain),
)
self.assertEqual(
base.joinpath(f"inventory/host_vars/{subdomain}-host/gitea.yml"),
infra._gitea_path(subdomain=subdomain),
)
self.assertEqual(
base.joinpath(f"inventory/host_vars/{subdomain}-host/provision.yml"),
infra._provision_path(subdomain=subdomain),
)
self.assertEqual(
base.joinpath(f"inventory/{subdomain}-backup.yml"),
infra._backup_path(subdomain=subdomain),
)
self.assertEqual(
base.joinpath(f"inventory/hosts-scripts/{subdomain}-host.sh"),
infra._hostscript_path(subdomain=subdomain),
)

181
infrastructure/utils.py Normal file
View File

@ -0,0 +1,181 @@
# 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.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.index.add(self._host_vars_dir(subdomain=subdomain))
self.repo.index.add(self._backup_path(subdomain=subdomain))
self.repo.index.add(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(
f"{action} VM {subdomain}", author="bot@dashboard.hostea.org"
)
def add_vm(self, instance: Instance):
"""
Add new VM to infrastructure repository
"""
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)
gitea = self._gitea_path(subdomain)
gitea_template = "./templates/infrastructure/yml/gitea.yml"
shutil.copy(gitea_template, gitea)
# 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
with open(provision, "w", encoding="utf-8") as f:
f.write(
render_to_string(
"infrastructure/yml/provision.yml",
context={"vm_size": instance.instance_id.name},
)
)
# 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/create.sh", context={"subdomain": subdomain}
),
)
self._commit(action="add", subdomain=subdomain)
def remove_vm(self, instance: Instance):
"""
Remove a VM from infrastructure repository
"""
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/rm.sh", context={"subdomain": subdomain}
),
)
self._commit(action="rm", subdomain=subdomain)